diff --git a/playground/gradients/GradientQuickShape.cpp b/playground/gradients/GradientQuickShape.cpp new file mode 100644 index 00000000..c002b236 --- /dev/null +++ b/playground/gradients/GradientQuickShape.cpp @@ -0,0 +1,204 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "GradientQuickShape.h" + +#include +#include + +QSK_QT_PRIVATE_BEGIN + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + #ifndef signals + #define signals Q_SIGNALS + #endif +#endif + +#include +#include +#include + +QSK_QT_PRIVATE_END + +namespace +{ + class ShapePath : public QQuickShapePath + { + public: + ShapePath( QObject* parent = nullptr ) + : QQuickShapePath( parent ) + { + setStrokeWidth( 0 ); + } + + void setRect( const QRectF& rect ) + { + auto& path = QQuickShapePathPrivate::get( this )->_path; + + path.clear(); + path.addRect( rect ); + } + + void setGradient( const QRectF& rect, const QskGradient& gradient ) + { + auto d = QQuickShapePathPrivate::get( this ); + + delete d->sfp.fillGradient; + + d->sfp.fillGradient = createShapeGradient( rect, gradient ); + d->sfp.fillGradient->setParent( this ); + } + + private: + QQuickShapeGradient* createShapeGradient( + const QRectF& rect, const QskGradient& gradient ) const + { + + QQuickShapeGradient* shapeGradient = nullptr; + + const auto qtGradient = gradient.toQGradient( rect ); + + switch( qtGradient.type() ) + { + case QGradient::LinearGradient: + { + auto& linearGradient = + *static_cast< const QLinearGradient* >( &qtGradient ); + + auto g = new QQuickShapeLinearGradient(); + + g->setX1( linearGradient.start().x() ); + g->setY1( linearGradient.start().y() ); + g->setX2( linearGradient.finalStop().x() ); + g->setY2( linearGradient.finalStop().y() ); + + shapeGradient = g; + break; + } + + case QGradient::RadialGradient: + { + auto& radialGradient = + *static_cast< const QRadialGradient* >( &qtGradient ); + + auto g = new QQuickShapeRadialGradient(); + + g->setCenterX( radialGradient.center().x() ); + g->setCenterY( radialGradient.center().y() ); + g->setFocalX( radialGradient.center().x() ); + g->setFocalY( radialGradient.center().y() ); + + g->setCenterRadius( radialGradient.radius() ); + g->setFocalRadius( radialGradient.radius() ); + + shapeGradient = g; + break; + } + + case QGradient::ConicalGradient: + { + auto& conicalGradient = + *static_cast< const QConicalGradient* >( &qtGradient ); + + auto g = new QQuickShapeConicalGradient(); + + g->setCenterX( conicalGradient.center().x() ); + g->setCenterY( conicalGradient.center().y() ); + g->setAngle( conicalGradient.angle() ); + + shapeGradient = g; + break; + } + + default: + break; + } + + shapeGradient->setSpread( + static_cast< QQuickShapeGradient::SpreadMode >( gradient.spread() ) ); + + /* + QQuickGradient has been made in the early days of Qt5 for the QML + use case. Everything - even each stop - is a QObject. + */ + const auto qtStops = qskToQGradientStops( gradient.stops() ); + + for ( const auto& stop : qtStops ) + { + class MyGradient : public QObject + { + public: + QList< QQuickGradientStop* > m_stops; + }; + + auto s = new QQuickGradientStop( shapeGradient ); + s->setPosition( stop.first ); + s->setColor( stop.second ); + + reinterpret_cast< MyGradient* >( shapeGradient )->m_stops += s; + } + + return shapeGradient; + } + }; + + class ShapeItem : public QQuickShape + { + public: + ShapeItem() + { + auto d = QQuickShapePrivate::get( this ); + d->sp += new ShapePath( this ); + } + + QSGNode* updateShapeNode( QQuickWindow* window, const QRectF& rect, + const QskGradient& gradient, QSGNode* node ) + { + auto d = QQuickShapePrivate::get( this ); + + ShapePath path; + path.setRect( rect ); + path.setGradient( rect, gradient ); + + d->sp += &path; + d->spChanged = true; + + d->refWindow( window ); + updatePolish(); + node = QQuickShape::updatePaintNode( node, nullptr ); + d->derefWindow(); + + d->sp.clear(); + + return node; + } + + private: + QSGNode* updatePaintNode( QSGNode*, UpdatePaintNodeData* ) override + { + Q_ASSERT( false ); + return nullptr; + } + }; +} + +Q_GLOBAL_STATIC( ShapeItem, shapeItem ) + +QSGNode* GradientQuickShape::updateNode( QQuickWindow* window, + const QRectF& rect, const QskGradient& gradient, QSGNode* node ) +{ + /* + Unfortunately the different materials for the gradients are hidden + in private classes of the quickshape module, and can't be accessed + from application code. Hard to understand why such basic functionality + is not offered like QSGFlatColorMaterial and friends. Anyway - we have + QskGradientMaterial now ... + + But for the purpose of comparing our shaders with those from quickshape we + use a static QQuickShape to create/update scene graph node, that actually + belong to a different QQuickItem. + */ + return shapeItem->updateShapeNode( window, rect, gradient, node ); +} diff --git a/playground/gradients/GradientQuickShape.h b/playground/gradients/GradientQuickShape.h new file mode 100644 index 00000000..9a3f662a --- /dev/null +++ b/playground/gradients/GradientQuickShape.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#pragma once + +class QskGradient; +class QSGNode; +class QQuickWindow; +class QRectF; + +namespace GradientQuickShape +{ + QSGNode* updateNode( QQuickWindow*, const QRectF&, + const QskGradient&, QSGNode* ); +} diff --git a/playground/gradients/GradientView.cpp b/playground/gradients/GradientView.cpp new file mode 100644 index 00000000..377ba676 --- /dev/null +++ b/playground/gradients/GradientView.cpp @@ -0,0 +1,132 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "GradientView.h" + +#ifdef SHAPE_GRADIENT + #include "GradientQuickShape.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ + template< typename Node > + inline Node* gradientNode( QSGNode* node ) + { + if ( node == nullptr ) + node = new Node(); + + return static_cast< Node* >( node ); + } + + class PaintedNode final : public QskPaintedNode + { + public: + void updateNode( QQuickWindow* window, + const QRectF& rect, const QskGradient& gradient ) + { + update( window, rect, QSizeF(), &gradient ); + } + + QskHashValue hash( const void* nodeData ) const override + { + const auto gradient = reinterpret_cast< const QskGradient* >( nodeData ); + return gradient->hash(); + } + + protected: + void paint( QPainter* painter, const QSize& size, const void* nodeData ) override + { + const auto gradient = reinterpret_cast< const QskGradient* >( nodeData ); + const QRect rect( 0, 0, size.width(), size.height() ); + + painter->fillRect( rect, gradient->toQGradient( rect ) ); + } + }; +} + +GradientView::GradientView( NodeType nodeType, QQuickItem* parent ) + : QQuickItem( parent ) + , m_nodeType( nodeType ) +{ + setFlag( QQuickItem::ItemHasContents, true ); +} + +GradientView::NodeType GradientView::GradientView::nodeType() const +{ + return m_nodeType; +} + +void GradientView::setGradient( const QskGradient& gradient ) +{ + m_gradient = gradient; + update(); +} + +QskGradient GradientView::gradient() const +{ + return m_gradient; +} + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) +void GradientView::geometryChange( const QRectF&, const QRectF& ) +#else +void GradientView::geometryChanged( const QRectF&, const QRectF& ) +#endif +{ + update(); +} + +QSGNode* GradientView::updatePaintNode( + QSGNode* oldNode, QQuickItem::UpdatePaintNodeData* ) +{ + const QRectF rect( 0, 0, width(), height() ); + + switch( m_nodeType ) + { + case Painted: + { + auto node = gradientNode< PaintedNode >( oldNode ); + node->updateNode( window(), rect, m_gradient ); + + return node; + } + case Rectangle: + { + auto node = gradientNode< QskRectangleNode >( oldNode ); + node->updateNode( rect, m_gradient ); + + return node; + } + case BoxFill: + { + auto node = gradientNode< QskBoxFillNode >( oldNode ); + node->updateNode( rect, m_gradient ); + + return node; + } +#ifdef SHAPE_GRADIENT + case Shape: + { + return GradientQuickShape::updateNode( + window(), rect, m_gradient, oldNode ); + } +#endif + default: + break; + } + + return nullptr; +} + +#include "moc_GradientView.cpp" diff --git a/playground/gradients/GradientView.h b/playground/gradients/GradientView.h new file mode 100644 index 00000000..f0c7ebe4 --- /dev/null +++ b/playground/gradients/GradientView.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#pragma once + +#include +#include + +class GradientView : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY( QskGradient gradient READ gradient WRITE setGradient ) + + public: + enum NodeType + { + Painted, + Rectangle, + BoxFill, +#ifdef SHAPE_GRADIENT + Shape, +#endif + + NumNodeTypes + }; + Q_ENUM( NodeType ) + + GradientView( NodeType, QQuickItem* parent = nullptr ); + + NodeType nodeType() const; + + void setGradient( const QskGradient& ); + QskGradient gradient() const; + + protected: + QSGNode* updatePaintNode( QSGNode*, QQuickItem::UpdatePaintNodeData* ) override; +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + void geometryChange( const QRectF&, const QRectF& ) override; +#else + void geometryChanged( const QRectF&, const QRectF& ) override; +#endif + + private: + const NodeType m_nodeType; + QskGradient m_gradient; +}; diff --git a/playground/gradients/gradients.pro b/playground/gradients/gradients.pro index 3cd3c87b..4bd9e8f8 100644 --- a/playground/gradients/gradients.pro +++ b/playground/gradients/gradients.pro @@ -1,16 +1,18 @@ CONFIG += qskexample -QT += widgets quickwidgets - -HEADERS += +HEADERS += \ + GradientView.h SOURCES += \ + GradientView.cpp \ main.cpp -linux { +qtHaveModule(quickshapes_private) { - pedantic { - QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]/QtQuickWidgets - QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]/QtWidgets - } + QT += quickshapes_private + + HEADERS += GradientQuickShape.h + SOURCES += GradientQuickShape.cpp + + DEFINES += SHAPE_GRADIENT } diff --git a/playground/gradients/main.cpp b/playground/gradients/main.cpp index 02c9dc49..032430c9 100644 --- a/playground/gradients/main.cpp +++ b/playground/gradients/main.cpp @@ -3,147 +3,79 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ +#include "GradientView.h" + #include -#include + #include +#include +#include -#include -#include -#include -#include -#include +#include -class BoxQsk: public QQuickWidget +namespace { - public: - BoxQsk( QWidget* parent = nullptr ) - : QQuickWidget( parent ) + class MainView : public QskLinearBox { - setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); + Q_OBJECT - setContentsMargins( QMargins() ); - setResizeMode( QQuickWidget::SizeRootObjectToView ); - - m_control = new QskControl(); - setContent( QUrl(), nullptr, m_control ); - } - - void showGradient( const QGradient& gradient ) - { - QskGradient qskGradient( gradient ); - - /* - Eliminate the useless offsets that have been added to work around - QGradients limitation to have stops at the same position - */ - - auto stops = qskGradient.stops(); - for ( int i = 1; i < stops.count(); i++ ) + public: + MainView( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, 2, parent ) { - if ( stops[i].position() - stops[i-1].position() < 1e-5 ) - stops[i].setPosition( stops[i-1].position() ); + for ( int i = 0; i < GradientView::NumNodeTypes; i++ ) + { + const auto nodeType = static_cast< GradientView::NodeType >( i ); + m_views[i] = new GradientView( nodeType, this ); + } + + showColors( { Qt::green, Qt::red, Qt::yellow, Qt::cyan, Qt::darkCyan } ); } - qskGradient.setStops( stops ); - - m_control->setBackground( qskGradient ); - } - - private: - QskControl* m_control; -}; - -class BoxQt: public QWidget -{ - public: - BoxQt( QWidget* parent = nullptr ) - : QWidget( parent ) - { - setContentsMargins( QMargins() ); - setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); - } - - void showGradient( const QGradient& gradient ) - { - m_gradient = gradient; - m_gradient.setCoordinateMode( QGradient::ObjectMode ); - - update(); - } - - protected: - void paintEvent( QPaintEvent* ) override - { - QPainter painter( this ); - painter.fillRect( contentsRect(), m_gradient ); - } - - private: - QGradient m_gradient; -}; - -class GradientBox: public QWidget -{ - Q_OBJECT - - public: - GradientBox( QWidget* parent = nullptr ) - : QWidget( parent ) - { - m_qtBox = new BoxQt(); - m_qskBox = new BoxQsk(); - - auto layout = new QHBoxLayout( this ); - layout->addWidget( m_qtBox ); - layout->addWidget( m_qskBox ); - - showGradient( { Qt::green, Qt::red, Qt::yellow, Qt::cyan, Qt::darkCyan } ); - } - - void showGradient( const QVector< QColor >& colors ) - { - const auto step = 1.0 / colors.size(); - - QGradientStops stops; - - for ( int i = 0; i < colors.size(); i++ ) + void showColors( const QVector< QColor >& colors ) { - auto pos = i * step; - if ( i > 0 ) - pos += 1e-6; // QGradient does not support stops at the same position + const auto step = 1.0 / colors.size(); - stops += { pos, colors[i] }; - stops += { ( i + 1 ) * step, colors[i] }; + QskGradientStops stops; + + for ( int i = 0; i < colors.size(); i++ ) + { + stops += { i * step, colors[i] }; + stops += { ( i + 1 ) * step, colors[i] }; + } + + QskGradient gradient; + gradient.setLinearDirection( 0.0, 0.0, 1.0, 1.0 ); + gradient.setSpread( QskGradient::RepeatSpread ); + gradient.setStops( stops ); + + showGradient( gradient ); } - QLinearGradient gradient( 0.0, 0.0, 0.5, 0.5 ); - gradient.setSpread( QGradient::ReflectSpread ); - gradient.setStops( stops ); + public Q_SLOTS: + void showGradient( const QskGradient& gradient ) + { + for ( auto view : m_views ) + { + if ( view ) + view->setGradient( gradient ); + } + } - showGradient( gradient ); - } - - public Q_SLOTS: - void showGradient( const QGradient& gradient ) - { - m_qtBox->showGradient( gradient ); - m_qskBox->showGradient( gradient ); - } - - private: - BoxQt* m_qtBox; - BoxQsk* m_qskBox; -}; + private: + GradientView* m_views[ GradientView::NumNodeTypes ]; + }; +} int main( int argc, char** argv ) { - QApplication app( argc, argv ); + QGuiApplication app( argc, argv ); Skinny::init(); // we need a skin - GradientBox box; - - box.resize( 600, 600 ); - box.show(); + QskWindow window; + window.addItem( new MainView() ); + window.resize( 600, 600 ); + window.show(); return app.exec(); } diff --git a/playground/playground.pro b/playground/playground.pro index 509d40a3..c464205e 100644 --- a/playground/playground.pro +++ b/playground/playground.pro @@ -4,6 +4,7 @@ SUBDIRS += \ anchors \ dials \ dialogbuttons \ + gradients \ invoker \ inputpanel \ images \ @@ -19,6 +20,5 @@ qtHaveModule(webengine) { qtHaveModule(quickwidgets) { SUBDIRS += \ - grids \ - gradients + grids }