diff --git a/cmake/QskFindMacros.cmake b/cmake/QskFindMacros.cmake index f2791254..88adb67e 100644 --- a/cmake/QskFindMacros.cmake +++ b/cmake/QskFindMacros.cmake @@ -54,16 +54,29 @@ macro(qsk_setup_Qt) # C++, but QSkinny itself does not need the WebEngine at all. if (QT_VERSION_MAJOR VERSION_LESS 6) - find_package(Qt${QT_VERSION_MAJOR} QUIET OPTIONAL_COMPONENTS WebEngine) + find_package(Qt${QT_VERSION_MAJOR} QUIET + OPTIONAL_COMPONENTS WebEngine) + + find_package(Qt${QT_VERSION_MAJOR} QUIET + OPTIONAL_COMPONENTS QuickShapes) + set( Qt5WebEngineQuick_FOUND ${Qt5WebEngine_FOUND} ) + set( Qt5QuickShapesPrivate_FOUND ${Qt5QuickShapes_FOUND} ) else() find_package(Qt${QT_VERSION_MAJOR} QUIET OPTIONAL_COMPONENTS WebEngineCore WebEngineQuick) + + find_package(Qt${QT_VERSION_MAJOR} QUIET + OPTIONAL_COMPONENTS QuickShapesPrivate) endif() if( NOT Qt${QT_VERSION_MAJOR}WebEngineQuick_FOUND) message(STATUS "No Qt/Quick WebEngine support: skipping some unimportant examples") endif() + + if (NOT Qt${QT_VERSION_MAJOR}QuickShapesPrivate_FOUND) + message(STATUS "No Qt/Quick Shapes support: skipping some unimportant examples") + endif() endif() endmacro() diff --git a/examples/frames/Frame.cpp b/examples/frames/Frame.cpp index ecbfcb1f..4c89f3d3 100644 --- a/examples/frames/Frame.cpp +++ b/examples/frames/Frame.cpp @@ -150,7 +150,7 @@ void Frame::updateFrameNode( const QRectF& rect, QskBoxRectangleNode* node ) const QskBoxBorderColors borderColors( c1, c1, c2, c2 ); const qreal radius = effectiveRadius( rect, m_radius ); - node->updateNode( rect, radius, m_frameWidth, borderColors, m_color ); + node->updateBox( rect, radius, m_frameWidth, borderColors, m_color ); } #include "moc_Frame.cpp" diff --git a/examples/iotdashboard/DiagramSkinlet.cpp b/examples/iotdashboard/DiagramSkinlet.cpp index 964013e2..5e2ab79f 100644 --- a/examples/iotdashboard/DiagramSkinlet.cpp +++ b/examples/iotdashboard/DiagramSkinlet.cpp @@ -173,7 +173,7 @@ QSGNode* DiagramSkinlet::updateChartNode( const Diagram* diagram, QSGNode* node color = diagram->color( barSubcontrol ); const auto shape = diagram->boxShapeHint( barSubcontrol ); - barNode->updateNode( barRect, shape, {}, {}, color ); + barNode->updateFilling( barRect, shape, color ); } } else diff --git a/playground/gradients/CMakeLists.txt b/playground/gradients/CMakeLists.txt index cdbe015b..6fca9402 100644 --- a/playground/gradients/CMakeLists.txt +++ b/playground/gradients/CMakeLists.txt @@ -5,13 +5,14 @@ set(SOURCES GradientView.h GradientView.cpp main.cpp) -if(TARGET quickshapes_private) +if(TARGET Qt::QuickShapesPrivate) list(APPEND SOURCES GradientQuickShape.h GradientQuickShape.cpp) endif() -qsk_add_example(gradients ${SOURCES}) +set(target gradients) +qsk_add_example(${target} ${SOURCES}) -if(TARGET quickshapes_private) - target_compile_definitions(gradients PRIVATE SHAPE_GRADIENT) - target_link_libraries(gradients PRIVATE quickshapes_private) +if(TARGET Qt::QuickShapesPrivate) + target_compile_definitions(${target} PRIVATE SHAPE_GRADIENT) + target_link_libraries(${target} PRIVATE Qt::QuickShapesPrivate ) endif() diff --git a/playground/gradients/GradientQuickShape.cpp b/playground/gradients/GradientQuickShape.cpp index 4261920a..9ace9733 100644 --- a/playground/gradients/GradientQuickShape.cpp +++ b/playground/gradients/GradientQuickShape.cpp @@ -14,7 +14,6 @@ QSK_QT_PRIVATE_BEGIN #define signals Q_SIGNALS #endif -#include #include #include diff --git a/playground/gradients/GradientView.cpp b/playground/gradients/GradientView.cpp index b067955e..78d90fed 100644 --- a/playground/gradients/GradientView.cpp +++ b/playground/gradients/GradientView.cpp @@ -10,7 +10,6 @@ #endif #include -#include #include #include #include @@ -75,20 +74,20 @@ namespace switch( nodeType ) { case GradientView::Painted: - text = "QskPaintedNode"; + text = "Raster PaintEngine"; break; case GradientView::BoxRectangle: - text = "QskBoxRectangleNode"; + text = "Colored Vertices"; break; case GradientView::BoxFill: - text = "QskBoxFillNode"; + text = "Qskinny Shader"; break; #ifdef SHAPE_GRADIENT case GradientView::Shape: - text = "QQuickShapeGenericNode"; + text = "Qt/Quick Shape Shader"; break; #endif @@ -149,6 +148,11 @@ QSGNode* GradientView::updatePaintNode( { const QRectF rect( 0, 0, width(), height() ); + QskBoxShapeMetrics shape; +#if 0 + shape.setRadius( 80 ); +#endif + switch( m_nodeType ) { case Painted: @@ -160,24 +164,17 @@ QSGNode* GradientView::updatePaintNode( } case BoxFill: { - auto node = gradientNode< QskBoxFillNode >( oldNode ); - node->updateNode( rect, m_gradient ); + auto node = gradientNode< QskBoxRectangleNode >( oldNode ); + node->setHint( QskFillNode::PreferColoredGeometry, false ); + node->updateFilling( rect, shape, m_gradient ); return node; } case BoxRectangle: { - QskBoxShapeMetrics shape; - shape.setRadius( 80 ); - - if ( !QskBoxRenderer::isGradientSupported( shape, m_gradient ) ) - { - delete oldNode; - return nullptr; - } - auto node = gradientNode< QskBoxRectangleNode >( oldNode ); - node->updateNode( rect, shape, m_gradient ); + node->setHint( QskFillNode::PreferColoredGeometry, true ); + node->updateFilling( rect, shape, m_gradient ); return node; } diff --git a/playground/webview/main.cpp b/playground/webview/main.cpp index f4a4294b..68b3dcbd 100644 --- a/playground/webview/main.cpp +++ b/playground/webview/main.cpp @@ -40,9 +40,9 @@ int main( int argc, char* argv[] ) #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) // namespace QtWebEngine doesn't exist in Qt6: https://doc.qt.io/qt-5/qtwebengine.html QtWebEngine::initialize(); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app( argc, argv ); SkinnyShortcut::enable( SkinnyShortcut::Quit | SkinnyShortcut::DebugShortcuts ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b774c94..44198f79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -107,7 +107,6 @@ list(APPEND HEADERS nodes/QskBasicLinesNode.h nodes/QskBoxNode.h nodes/QskBoxClipNode.h - nodes/QskBoxFillNode.h nodes/QskBoxRectangleNode.h nodes/QskBoxRenderer.h nodes/QskBoxMetrics.h @@ -149,7 +148,6 @@ list(APPEND SOURCES nodes/QskBasicLinesNode.cpp nodes/QskBoxNode.cpp nodes/QskBoxClipNode.cpp - nodes/QskBoxFillNode.cpp nodes/QskBoxRectangleNode.cpp nodes/QskBoxRenderer.cpp nodes/QskBoxMetrics.cpp @@ -164,7 +162,6 @@ list(APPEND SOURCES nodes/QskLinesNode.cpp nodes/QskPaintedNode.cpp nodes/QskPlainTextRenderer.cpp - nodes/QskRectangleNode.cpp nodes/QskRichTextRenderer.cpp nodes/QskSceneTexture.cpp nodes/QskSGNode.cpp diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 48c456a9..a9d74074 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -22,7 +22,6 @@ #include "QskGraphicNode.h" #include "QskGraphic.h" #include "QskLinesNode.h" -#include "QskRectangleNode.h" #include "QskSGNode.h" #include "QskStippleMetrics.h" #include "QskTextColors.h" @@ -373,8 +372,9 @@ QSGNode* QskSkinlet::updateBackgroundNode( if ( !gradient.isValid() ) return nullptr; - auto rectNode = QskSGNode::ensureNode< QskRectangleNode >( node ); - rectNode->updateNode( rect, gradient ); + auto rectNode = QskSGNode::ensureNode< QskBoxRectangleNode >( node ); + rectNode->updateFilling( rect, QskBoxShapeMetrics(), + QskBoxBorderMetrics(), gradient ); return rectNode; } diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index da21c40b..b204cbd8 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -14,8 +14,12 @@ QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskTextInput, Panel ) QSK_SUBCONTROL( QskTextInput, Text ) + +#if 1 +// shouldn't this be a Selected state, TODO ... QSK_SUBCONTROL( QskTextInput, PanelSelected ) QSK_SUBCONTROL( QskTextInput, TextSelected ) +#endif QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 ) @@ -458,6 +462,9 @@ void QskTextInput::focusInEvent( QFocusEvent* event ) break; default: +#if 1 + // auto selecting the complete text ??? +#endif setEditing( true ); } } diff --git a/src/nodes/QskBoxFillNode.cpp b/src/nodes/QskBoxFillNode.cpp deleted file mode 100644 index 2da882c5..00000000 --- a/src/nodes/QskBoxFillNode.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "QskBoxFillNode.h" -#include "QskGradientMaterial.h" -#include "QskGradient.h" -#include "QskGradientDirection.h" -#include "QskBoxShapeMetrics.h" -#include "QskBoxBorderMetrics.h" -#include "QskBoxRenderer.h" -#include "QskFillNodePrivate.h" - -static inline QskHashValue qskMetricsHash( - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) -{ - QskHashValue hash = 13000; - - hash = shape.hash( hash ); - return borderMetrics.hash( hash ); -} - -class QskBoxFillNodePrivate final : public QskFillNodePrivate -{ - public: - inline void resetValues() - { - rect = QRectF(); - gradientHash = 0; - metricsHash = 0; - } - - QRectF rect; - - QskHashValue gradientHash = 0; - QskHashValue metricsHash = 0; -}; - -QskBoxFillNode::QskBoxFillNode() - : QskFillNode( *new QskBoxFillNodePrivate ) -{ -} - -void QskBoxFillNode::updateNode( const QRectF& rect, const QskGradient& gradient ) -{ - updateNode( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), gradient ); -} - -void QskBoxFillNode::updateNode( - const QRectF& rect, const QskBoxShapeMetrics& shapeMetrics, - const QskBoxBorderMetrics& borderMetrics, const QskGradient& gradient ) -{ - Q_D( QskBoxFillNode ); - - if ( rect.isEmpty() || !gradient.isVisible() ) - { - d->resetValues(); - resetGeometry(); - - return; - } - - const auto metricsHash = qskMetricsHash( shapeMetrics, borderMetrics ); - const auto gradientHash = gradient.hash( 22879 ); - - const bool dirtyColors = gradientHash != d->gradientHash; - const bool dirtyMetrics = ( metricsHash != d->metricsHash ) || ( rect != d->rect ); - - d->metricsHash = metricsHash; - d->gradientHash = gradientHash; - d->rect = rect; - - if ( dirtyMetrics ) - { - QskBoxRenderer::renderFillGeometry( - rect, shapeMetrics, borderMetrics, *geometry() ); - - markDirty( QSGNode::DirtyGeometry ); - geometry()->markVertexDataDirty(); - } - - if ( gradient.isMonochrome() ) - { - if ( dirtyColors ) - setColoring( gradient.startColor().toRgb() ); - } - else - { - // dirtyMetrics: the shader also depends on the target rectangle ! - - if ( dirtyColors || dirtyMetrics ) - setColoring( rect, gradient.effectiveGradient() ); - } -} diff --git a/src/nodes/QskBoxFillNode.h b/src/nodes/QskBoxFillNode.h deleted file mode 100644 index d43b82d7..00000000 --- a/src/nodes/QskBoxFillNode.h +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_BOX_FILL_NODE_H -#define QSK_BOX_FILL_NODE_H - -#include "QskGlobal.h" -#include "QskFillNode.h" - -class QskGradient; -class QskBoxShapeMetrics; -class QskBoxBorderMetrics; - -class QskBoxFillNodePrivate; - -class QSK_EXPORT QskBoxFillNode : public QskFillNode -{ - using Inherited = QskFillNode; - - public: - QskBoxFillNode(); - - void updateNode( const QRectF&, - const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, - const QskGradient& ); - - void updateNode( const QRectF&, const QskGradient& ); - - private: - Q_DECLARE_PRIVATE( QskBoxFillNode ) -}; - -#endif diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index fbad3b89..5ed2a0cd 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -4,10 +4,8 @@ *****************************************************************************/ #include "QskBoxNode.h" -#include "QskBoxFillNode.h" #include "QskBoxShadowNode.h" #include "QskBoxRectangleNode.h" -#include "QskBoxRenderer.h" #include "QskSGNode.h" #include "QskGradient.h" @@ -67,7 +65,7 @@ void QskBoxNode::updateNode( const QRectF& rect, QskBoxShadowNode* shadowNode = nullptr; QskBoxRectangleNode* rectNode = nullptr; - QskBoxFillNode* fillNode = nullptr; + QskBoxRectangleNode* fillNode = nullptr; if ( !shadowMetrics.isNull() && shadowColor.isValid() && shadowColor.alpha() != 0 ) @@ -77,29 +75,23 @@ void QskBoxNode::updateNode( const QRectF& rect, shape, shadowMetrics.blurRadius(), shadowColor ); } - /* - QskBoxRectangleNode is more efficient and creates batchable geometries. - So we prefer using it where possible. - Note, that the border is always done with a QskBoxRectangleNode - */ - - if ( QskBoxRenderer::isGradientSupported( shape, gradient ) ) + if ( QskBoxRectangleNode::isCombinedGeometrySupported( gradient ) ) { rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole ); - rectNode->updateNode( rect, shape, borderMetrics, borderColors, gradient ); + rectNode->updateBox( rect, shape, borderMetrics, borderColors, gradient ); } else { if ( !borderMetrics.isNull() && borderColors.isVisible() ) { rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole ); - rectNode->updateNode( rect, shape, borderMetrics, borderColors, QskGradient() ); + rectNode->updateBorder( rect, shape, borderMetrics, borderColors ); } if ( gradient.isVisible() ) { - fillNode = qskNode< QskBoxFillNode >( this, FillRole ); - fillNode->updateNode( rect, shape, borderMetrics, gradient ); + fillNode = qskNode< QskBoxRectangleNode >( this, FillRole ); + fillNode->updateFilling( rect, shape, borderMetrics, gradient ); } } diff --git a/src/nodes/QskBoxRectangleNode.cpp b/src/nodes/QskBoxRectangleNode.cpp index da7c8a97..dd5c7fb0 100644 --- a/src/nodes/QskBoxRectangleNode.cpp +++ b/src/nodes/QskBoxRectangleNode.cpp @@ -12,46 +12,55 @@ #include "QskGradientDirection.h" #include "QskFillNodePrivate.h" -static inline QskHashValue qskMetricsHash( - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) -{ - QskHashValue hash = 13000; - - hash = shape.hash( hash ); - return borderMetrics.hash( hash ); -} - -static inline QskHashValue qskColorsHash( - const QskBoxBorderColors& borderColors, const QskGradient& fillGradient ) -{ - QskHashValue hash = 13000; - hash = borderColors.hash( hash ); - return fillGradient.hash( hash ); -} - -#if 1 - -static inline QskGradient qskEffectiveGradient( const QskGradient& gradient ) -{ - auto g = gradient.effectiveGradient(); - - if ( g.type() != QskGradient::Linear ) - { - qWarning() << "QskBoxRectangleNode does not support radial/conic gradients"; - g.setDirection( QskGradient::Linear ); - } - - return g; -} - -#endif - class QskBoxRectangleNodePrivate final : public QskFillNodePrivate { public: - QskHashValue metricsHash = 0; - QskHashValue colorsHash = 0; - QRectF rect; + inline void resetNode( QskBoxRectangleNode* node ) + { + m_metricsHash = m_colorsHash = 0; + node->resetGeometry(); + } + + bool updateMetrics( const QRectF& rect, + const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) + { + QskHashValue hash = 13000; + + hash = qHashBits( &rect, sizeof( rect ), hash ); + hash = shape.hash( hash ); + hash = borderMetrics.hash( hash ); + + return updateValue( m_metricsHash, hash ); + } + + bool updateColors( const QskBoxBorderColors& borderColors, const QskGradient& gradient ) + { + QskHashValue hash = 13000; + + if ( borderColors.isVisible() ) + hash = borderColors.hash( hash ); + + if ( gradient.isVisible() ) + hash = gradient.hash( hash ); + + return updateValue( m_colorsHash, hash ); + } + + private: + inline bool updateValue( QskHashValue& value, const QskHashValue newValue ) const + { + if ( newValue != value ) + { + value = newValue; + return true; + } + + return false; + } + + public: + QskHashValue m_metricsHash = 0; + QskHashValue m_colorsHash = 0; }; QskBoxRectangleNode::QskBoxRectangleNode() @@ -63,132 +72,163 @@ QskBoxRectangleNode::~QskBoxRectangleNode() { } -void QskBoxRectangleNode::updateNode( - const QRectF& rect, const QskGradient& fillGradient ) +void QskBoxRectangleNode::updateFilling( + const QRectF& rect, const QskGradient& gradient ) { - updateNode( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), - QskBoxBorderColors(), fillGradient ); + updateFilling( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), gradient ); } -void QskBoxRectangleNode::updateNode( const QRectF& rect, - const QskBoxShapeMetrics& shape, const QskGradient& fillGradient ) +void QskBoxRectangleNode::updateFilling( const QRectF& rect, + const QskBoxShapeMetrics& shape, const QskGradient& gradient ) { - updateNode( rect, shape, QskBoxBorderMetrics(), - QskBoxBorderColors(), fillGradient ); + updateFilling( rect, shape, QskBoxBorderMetrics(), gradient ); } -void QskBoxRectangleNode::updateNode( const QRectF& rect, +void QskBoxRectangleNode::updateFilling( const QRectF& rect, + const QskBoxShapeMetrics& shapeMetrics, const QskBoxBorderMetrics& borderMetrics, + const QskGradient& gradient ) +{ + Q_D( QskBoxRectangleNode ); + + if ( rect.isEmpty() || !gradient.isVisible() ) + { + d->resetNode( this ); + return; + } + + const auto fillGradient = gradient.effectiveGradient(); + const auto shape = shapeMetrics.toAbsolute( rect.size() ); + + const bool coloredGeometry = hasHint( PreferColoredGeometry ) + && QskBoxRenderer::isGradientSupported( fillGradient ); + + const bool dirtyMetrics = d->updateMetrics( rect, shape, borderMetrics ); + const bool dirtyColors = d->updateColors( QskBoxBorderColors(), fillGradient ) + && ( coloredGeometry == isGeometryColored() ); + + if ( dirtyMetrics || dirtyColors ) + { + if ( coloredGeometry ) + { + setColoring( QskFillNode::Polychrome ); + + QskBoxRenderer::renderBox( rect, shape, + borderMetrics, QskBoxBorderColors(), fillGradient, *geometry() ); + + markDirty( QSGNode::DirtyGeometry ); + } + else + { + if ( fillGradient.isMonochrome() ) + { + if ( dirtyColors ) + setColoring( fillGradient.rgbStart() ); + } + else + { + // dirtyMetrics: the shader also depends on rect ! + setColoring( rect, fillGradient ); + } + + if ( dirtyMetrics ) + { + QskBoxRenderer::renderFillGeometry( + rect, shape, borderMetrics, *geometry() ); + + markDirty( QSGNode::DirtyGeometry ); + } + } + } +} + +void QskBoxRectangleNode::updateBorder( const QRectF& rect, + const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, + const QskBoxBorderColors& borderColors ) +{ + Q_D( QskBoxRectangleNode ); + + if ( rect.isEmpty() || borderMetrics.isNull() || !borderColors.isVisible() ) + { + d->resetNode( this ); + return; + } + + const bool dirtyMetrics = d->updateMetrics( rect, shape, borderMetrics ); + const bool dirtyColors = d->updateColors( borderColors, QskGradient() ); + + if ( dirtyMetrics || dirtyColors ) + { + const auto coloring = QskFillNode::Polychrome; + + if ( coloring == QskFillNode::Polychrome ) + setColoring( coloring ); + else + setColoring( borderColors.left().rgbStart() ); + + QskBoxRenderer::renderBox( rect, shape, borderMetrics, + borderColors, QskGradient(), *this->geometry() ); + + markDirty( QSGNode::DirtyGeometry ); + } +} + +void QskBoxRectangleNode::updateBox( const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors, const QskGradient& gradient ) { Q_D( QskBoxRectangleNode ); - /* - We supports certain linear gradients only. - Everything else has to be done using QskRectangleNode - */ - const auto fillGradient = qskEffectiveGradient( gradient ); - - const auto metricsHash = qskMetricsHash( shape, borderMetrics ); - const auto colorsHash = qskColorsHash( borderColors, fillGradient ); - - if ( ( metricsHash == d->metricsHash ) && - ( colorsHash == d->colorsHash ) && ( rect == d->rect ) ) - { - return; - } - - d->metricsHash = metricsHash; - d->colorsHash = colorsHash; - d->rect = rect; - - markDirty( QSGNode::DirtyMaterial ); - markDirty( QSGNode::DirtyGeometry ); - if ( rect.isEmpty() ) { - resetGeometry(); + d->resetNode( this ); return; } - bool hasFill = fillGradient.isVisible(); - - bool hasBorder = !borderMetrics.isNull(); - if ( hasBorder ) - { - /* - Wrong as the border width should have an - effect - even if not being visible. TODO ... - */ - - hasBorder = borderColors.isVisible(); - } - - if ( !hasBorder && !hasFill ) - { - resetGeometry(); - return; - } - - const bool isFillMonochrome = hasFill ? fillGradient.isMonochrome() : true; - const bool isBorderMonochrome = hasBorder ? borderColors.isMonochrome() : true; + const bool hasFill = gradient.isVisible(); + const bool hasBorder = !borderMetrics.isNull() && borderColors.isVisible(); if ( hasFill && hasBorder ) { - if ( isFillMonochrome && isBorderMonochrome ) + const bool dirtyMetrics = d->updateMetrics( rect, shape, borderMetrics ); + const bool dirtyColors = d->updateColors( borderColors, gradient ); + + if ( dirtyMetrics || dirtyColors ) { - if ( borderColors.left().rgbStart() == fillGradient.rgbStart() ) + /* + For monochrome border/fiiling with the same color we might be + able to do QskFillNode::Monochrome. However this is not implemeted in + QskBoxRenderer yet. TODO ... + */ + setColoring( QskFillNode::Polychrome ); + + auto fillGradient = gradient.effectiveGradient(); + if ( !QskBoxRenderer::isGradientSupported( fillGradient ) ) { - // we can draw border and background in one - hasBorder = false; + qWarning() << "QskBoxRenderer does not support radial/conic gradients"; + fillGradient.setDirection( QskGradient::Linear ); } + + QskBoxRenderer::renderBox( rect, shape, borderMetrics, + borderColors, fillGradient, *geometry() ); + + markDirty( QSGNode::DirtyGeometry ); } } - - auto coloring = QskFillNode::Polychrome; - -#if 0 - /* - Always using the same material result in a better batching - but wastes some memory. when we have a solid color. - Maybe its worth to introduce a flag to control the behaviour, - but for the moment we go with performance. - */ - - if ( !( hasFill && hasBorder ) ) + else if ( hasFill ) { - if ( ( hasFill && isFillMonochrome ) - || ( hasBorder && !isBorderMonochrome ) - { - coloring = QskFillNode::Monochrome; - } + updateFilling( rect, shape, borderMetrics, gradient ); } -#endif - - auto& geometry = *this->geometry(); - - if ( coloring == QskFillNode::Polychrome ) + else if ( hasBorder ) { - setColoring( coloring ); - - QskBoxRenderer::renderBox( d->rect, shape, borderMetrics, - borderColors, fillGradient, geometry ); + updateBorder( rect, shape, borderMetrics, borderColors ); } else { - if ( hasFill ) - { - setColoring( fillGradient.rgbStart() ); - QskBoxRenderer::renderFillGeometry( - d->rect, shape, QskBoxBorderMetrics(), geometry ); - } - else - { - setColoring( borderColors.left().rgbStart() ); - QskBoxRenderer::renderBorderGeometry( - d->rect, shape, borderMetrics, geometry ); - } + d->resetNode( this ); } - - geometry.markVertexDataDirty(); +} + +bool QskBoxRectangleNode::isCombinedGeometrySupported( const QskGradient& gradient ) +{ + return QskBoxRenderer::isGradientSupported( gradient ); } diff --git a/src/nodes/QskBoxRectangleNode.h b/src/nodes/QskBoxRectangleNode.h index cae20aab..362f1afe 100644 --- a/src/nodes/QskBoxRectangleNode.h +++ b/src/nodes/QskBoxRectangleNode.h @@ -24,15 +24,29 @@ class QSK_EXPORT QskBoxRectangleNode : public QskFillNode QskBoxRectangleNode(); ~QskBoxRectangleNode() override; - void updateNode( const QRectF&, + void updateBox( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, const QskBoxBorderColors&, const QskGradient& ); - void updateNode( const QRectF& rect, const QskGradient& ); + void updateBorder( const QRectF&, + const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, + const QskBoxBorderColors& ); - void updateNode( const QRectF& rect, + void updateFilling( const QRectF& rect, const QskGradient& ); + + void updateFilling( const QRectF& rect, const QskBoxShapeMetrics&, const QskGradient& ); + void updateFilling( const QRectF& rect, const QskBoxShapeMetrics&, + const QskBoxBorderMetrics&, const QskGradient& ); + + /* + If true border/filling can be rendered together into the same geometry. + This should not make much difference as the scene graph batches geometries + for the same material anyway. + */ + static bool isCombinedGeometrySupported( const QskGradient& ); + private: Q_DECLARE_PRIVATE( QskBoxRectangleNode ) }; diff --git a/src/nodes/QskBoxRenderer.cpp b/src/nodes/QskBoxRenderer.cpp index 3c9c73ea..a1e95e9f 100644 --- a/src/nodes/QskBoxRenderer.cpp +++ b/src/nodes/QskBoxRenderer.cpp @@ -34,6 +34,9 @@ static inline QskVertex::ColoredLine* qskAllocateColoredLines( static inline QskGradient qskEffectiveGradient( const QRectF& rect, const QskGradient& gradient ) { + if ( !gradient.isVisible() ) + return gradient; + const auto dir = gradient.linearDirection(); auto g = gradient; @@ -67,8 +70,7 @@ static inline bool qskMaybeSpreading( const QskGradient& gradient ) return true; } -bool QskBoxRenderer::isGradientSupported( - const QskBoxShapeMetrics&, const QskGradient& gradient ) +bool QskBoxRenderer::isGradientSupported( const QskGradient& gradient ) { if ( !gradient.isVisible() || gradient.isMonochrome() ) return true; @@ -112,6 +114,7 @@ void QskBoxRenderer::renderBorderGeometry( const QskBoxBorderMetrics& border, QSGGeometry& geometry ) { geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + geometry.markVertexDataDirty(); const QskBoxMetrics metrics( rect, shape, border ); const QskBoxBasicStroker stroker( metrics ); @@ -132,6 +135,7 @@ void QskBoxRenderer::renderFillGeometry( const QskBoxBorderMetrics& border, QSGGeometry& geometry ) { geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + geometry.markVertexDataDirty(); const QskBoxMetrics metrics( rect, shape, border ); QskBoxBasicStroker stroker( metrics ); @@ -154,6 +158,7 @@ void QskBoxRenderer::renderBox( const QRectF& rect, QSGGeometry& geometry ) { geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + geometry.markVertexDataDirty(); const QskBoxMetrics metrics( rect, shape, border ); const auto effectiveGradient = qskEffectiveGradient( metrics.innerRect, gradient ); diff --git a/src/nodes/QskBoxRenderer.h b/src/nodes/QskBoxRenderer.h index 1b6face6..06a4bb30 100644 --- a/src/nodes/QskBoxRenderer.h +++ b/src/nodes/QskBoxRenderer.h @@ -39,7 +39,7 @@ namespace QskBoxRenderer Filling the geometry usually with color information: see QSGGeometry::defaultAttributes_ColoredPoint2D() */ - QSK_EXPORT bool isGradientSupported( const QskBoxShapeMetrics&, const QskGradient& ); + QSK_EXPORT bool isGradientSupported( const QskGradient& ); QSK_EXPORT void renderBox( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, diff --git a/src/nodes/QskFillNode.cpp b/src/nodes/QskFillNode.cpp index bdb6ddc5..08930df3 100644 --- a/src/nodes/QskFillNode.cpp +++ b/src/nodes/QskFillNode.cpp @@ -115,7 +115,7 @@ void QskFillNode::setColoring( Coloring coloring ) QskFillNode::Coloring QskFillNode::coloring() const { - return d_func()->coloring; + return static_cast< QskFillNode::Coloring >( d_func()->coloring ); } void QskFillNode::setColoring( const QColor& color ) @@ -147,7 +147,34 @@ void QskFillNode::setColoring( const QRectF& rect, const QskGradient& gradient ) } } +void QskFillNode::setHint( Hint hint, bool on ) +{ + Q_D( QskFillNode ); + + if ( on ) + d->hints |= hint; + else + d->hints &= ~hint; +} + +void QskFillNode::setHints( Hints hints ) +{ + d_func()->hints = hints; +} + +QskFillNode::Hints QskFillNode::hints() const +{ + return static_cast< QskFillNode::Hints >( d_func()->hints ); +} + +bool QskFillNode::hasHint( Hint hint ) const +{ + return d_func()->hints & hint; +} + bool QskFillNode::isGeometryColored() const { return d_func()->geometry.attributeCount() != 1; } + +#include "moc_QskFillNode.cpp" diff --git a/src/nodes/QskFillNode.h b/src/nodes/QskFillNode.h index a2d7abac..2630bb0b 100644 --- a/src/nodes/QskFillNode.h +++ b/src/nodes/QskFillNode.h @@ -8,12 +8,15 @@ #include "QskGlobal.h" #include +#include class QskFillNodePrivate; class QskGradient; class QSK_EXPORT QskFillNode : public QSGGeometryNode { + Q_GADGET + using Inherited = QSGGeometryNode; public: @@ -27,6 +30,28 @@ class QSK_EXPORT QskFillNode : public QSGGeometryNode Conic }; + enum Hint + { + /* + Colors might be defined in the material ( QskGradientMaterial, + QSGFlatColorMaterial ) or attached to each point ( QSGVertexColorMaterial ). + + The main advantage of using colored points is, that the material becomes + independent of the coloring and the scene graph is able to batch the nodes + ( https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph.html ). + + However adding the color information for each point increases the memory + footprint. + + The default setting is to use colored points where possible. Note, that + this is what is also done in the Qt/Quick code. + */ + PreferColoredGeometry = 1 + }; + + Q_ENUM( Hint ) + Q_DECLARE_FLAGS( Hints, Hint ) + QskFillNode(); ~QskFillNode() override; @@ -35,11 +60,20 @@ class QSK_EXPORT QskFillNode : public QSGGeometryNode void setColoring( Coloring ); Coloring coloring() const; + void setColoring( QRgb ); void setColoring( const QColor& ); + void setColoring( Qt::GlobalColor ); + void setColoring( const QRectF&, const QskGradient& ); bool isGeometryColored() const; + void setHints( Hints ); + Hints hints() const; + + void setHint( Hint, bool on = true ); + bool hasHint( Hint ) const; + protected: QskFillNode( QskFillNodePrivate& ); @@ -47,4 +81,14 @@ class QSK_EXPORT QskFillNode : public QSGGeometryNode Q_DECLARE_PRIVATE( QskFillNode ) }; +inline void QskFillNode::setColoring( QRgb rgb ) +{ + setColoring( QColor::fromRgba( rgb ) ); +} + +inline void QskFillNode::setColoring( Qt::GlobalColor color ) +{ + setColoring( QColor( color ) ); +} + #endif diff --git a/src/nodes/QskFillNodePrivate.h b/src/nodes/QskFillNodePrivate.h index 2da5784b..93b48a35 100644 --- a/src/nodes/QskFillNodePrivate.h +++ b/src/nodes/QskFillNodePrivate.h @@ -19,6 +19,8 @@ class QskFillNodePrivate : public QSGGeometryNodePrivate public: inline QskFillNodePrivate() : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) + , coloring( QskFillNode::Polychrome ) + , hints( QskFillNode::PreferColoredGeometry ) { } @@ -26,7 +28,9 @@ class QskFillNodePrivate : public QSGGeometryNodePrivate friend class QskFillNode; QSGGeometry geometry; - QskFillNode::Coloring coloring = QskFillNode::Polychrome; + + uint coloring : 5; + uint hints : 3; }; #endif diff --git a/src/nodes/QskRectangleNode.cpp b/src/nodes/QskRectangleNode.cpp deleted file mode 100644 index e05dc871..00000000 --- a/src/nodes/QskRectangleNode.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "QskRectangleNode.h" -#include "QskGradient.h" -#include "QskBoxRenderer.h" -#include "QskBoxShapeMetrics.h" -#include "QskFillNodePrivate.h" - -class QskRectangleNodePrivate final : public QskFillNodePrivate -{ - public: - inline void resetValues() - { - rect = QRectF(); - shape = QskBoxShapeMetrics(); - gradientHash = 0; - metricsHash = 0; - } - - QRectF rect; - QskBoxShapeMetrics shape; - - QskHashValue gradientHash = 0; - QskHashValue metricsHash = 0; -}; - -QskRectangleNode::QskRectangleNode() - : QskFillNode( *new QskRectangleNodePrivate ) -{ -} - -QskRectangleNode::~QskRectangleNode() -{ -} - -void QskRectangleNode::updateNode( - const QRectF& rect, const QskGradient& gradient ) -{ - updateNode( rect, QskBoxShapeMetrics(), gradient ); -} - -void QskRectangleNode::updateNode( - const QRectF& rect, const QskBoxShapeMetrics& shape, const QskGradient& gradient ) -{ - Q_D( QskRectangleNode ); - - if ( rect.isEmpty() || !gradient.isVisible() ) - { - d->resetValues(); - resetGeometry(); - - return; - } - - const auto effectiveGradient = gradient.effectiveGradient(); - const auto effectiveShape = shape.toAbsolute( rect.size() ); - - const auto gradientHash = effectiveGradient.hash( 54228 ); - const auto metricsHash = effectiveShape.hash( 44564 ); - - const bool dirtyColors = gradientHash != d->gradientHash; - const bool dirtyMetrics = ( rect != d->rect ) || ( metricsHash != d->metricsHash ); - - if ( !( dirtyColors || dirtyMetrics ) ) - return; - - d->gradientHash = gradientHash; - d->metricsHash = metricsHash; - d->rect = rect; - d->shape = effectiveShape; - - if ( QskBoxRenderer::isGradientSupported( effectiveShape, effectiveGradient ) ) - { - setColoring( Polychrome ); - - /* - Colors are added to the vertices, while the material does - not depend on the gradient at all - */ - if ( dirtyMetrics || dirtyColors ) - { - QskBoxRenderer::renderBox( rect, - effectiveShape, effectiveGradient, *geometry() ); - - geometry()->markVertexDataDirty(); - markDirty( QSGNode::DirtyGeometry ); - } - } - else - { - if ( dirtyColors || dirtyMetrics ) - setColoring( rect, effectiveGradient ); - - if ( dirtyMetrics ) - { - QskBoxRenderer::renderFillGeometry( rect, effectiveShape, *geometry() ); - - geometry()->markVertexDataDirty(); - markDirty( QSGNode::DirtyGeometry ); - } - } -} diff --git a/src/nodes/QskRectangleNode.h b/src/nodes/QskRectangleNode.h deleted file mode 100644 index 5f2993be..00000000 --- a/src/nodes/QskRectangleNode.h +++ /dev/null @@ -1,34 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_RECTANGLE_NODE_H -#define QSK_RECTANGLE_NODE_H - -#include "QskGlobal.h" -#include "QskFillNode.h" - -class QskGradient; -class QskBoxShapeMetrics; -class QskRectangleNodePrivate; - -/* - QskRectangleNode is for rounded rectangles without a border. - */ -class QSK_EXPORT QskRectangleNode : public QskFillNode -{ - using Inherited = QskFillNode; - - public: - QskRectangleNode(); - ~QskRectangleNode() override; - - void updateNode( const QRectF&, const QskGradient& ); - void updateNode( const QRectF&, const QskBoxShapeMetrics&, const QskGradient& ); - - private: - Q_DECLARE_PRIVATE( QskRectangleNode ) -}; - -#endif