Merge branch 'master' into features/modelobjectbinder

This commit is contained in:
Uwe Rathmann 2024-09-17 17:43:06 +02:00
commit 69eae90d3a
22 changed files with 329 additions and 458 deletions

View File

@ -54,16 +54,29 @@ macro(qsk_setup_Qt)
# C++, but QSkinny itself does not need the WebEngine at all. # C++, but QSkinny itself does not need the WebEngine at all.
if (QT_VERSION_MAJOR VERSION_LESS 6) 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( Qt5WebEngineQuick_FOUND ${Qt5WebEngine_FOUND} )
set( Qt5QuickShapesPrivate_FOUND ${Qt5QuickShapes_FOUND} )
else() else()
find_package(Qt${QT_VERSION_MAJOR} QUIET find_package(Qt${QT_VERSION_MAJOR} QUIET
OPTIONAL_COMPONENTS WebEngineCore WebEngineQuick) OPTIONAL_COMPONENTS WebEngineCore WebEngineQuick)
find_package(Qt${QT_VERSION_MAJOR} QUIET
OPTIONAL_COMPONENTS QuickShapesPrivate)
endif() endif()
if( NOT Qt${QT_VERSION_MAJOR}WebEngineQuick_FOUND) if( NOT Qt${QT_VERSION_MAJOR}WebEngineQuick_FOUND)
message(STATUS "No Qt/Quick WebEngine support: skipping some unimportant examples") message(STATUS "No Qt/Quick WebEngine support: skipping some unimportant examples")
endif() endif()
if (NOT Qt${QT_VERSION_MAJOR}QuickShapesPrivate_FOUND)
message(STATUS "No Qt/Quick Shapes support: skipping some unimportant examples")
endif()
endif() endif()
endmacro() endmacro()

View File

@ -150,7 +150,7 @@ void Frame::updateFrameNode( const QRectF& rect, QskBoxRectangleNode* node )
const QskBoxBorderColors borderColors( c1, c1, c2, c2 ); const QskBoxBorderColors borderColors( c1, c1, c2, c2 );
const qreal radius = effectiveRadius( rect, m_radius ); 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" #include "moc_Frame.cpp"

View File

@ -173,7 +173,7 @@ QSGNode* DiagramSkinlet::updateChartNode( const Diagram* diagram, QSGNode* node
color = diagram->color( barSubcontrol ); color = diagram->color( barSubcontrol );
const auto shape = diagram->boxShapeHint( barSubcontrol ); const auto shape = diagram->boxShapeHint( barSubcontrol );
barNode->updateNode( barRect, shape, {}, {}, color ); barNode->updateFilling( barRect, shape, color );
} }
} }
else else

View File

@ -5,13 +5,14 @@
set(SOURCES GradientView.h GradientView.cpp main.cpp) set(SOURCES GradientView.h GradientView.cpp main.cpp)
if(TARGET quickshapes_private) if(TARGET Qt::QuickShapesPrivate)
list(APPEND SOURCES GradientQuickShape.h GradientQuickShape.cpp) list(APPEND SOURCES GradientQuickShape.h GradientQuickShape.cpp)
endif() endif()
qsk_add_example(gradients ${SOURCES}) set(target gradients)
qsk_add_example(${target} ${SOURCES})
if(TARGET quickshapes_private) if(TARGET Qt::QuickShapesPrivate)
target_compile_definitions(gradients PRIVATE SHAPE_GRADIENT) target_compile_definitions(${target} PRIVATE SHAPE_GRADIENT)
target_link_libraries(gradients PRIVATE quickshapes_private) target_link_libraries(${target} PRIVATE Qt::QuickShapesPrivate )
endif() endif()

View File

@ -14,7 +14,6 @@ QSK_QT_PRIVATE_BEGIN
#define signals Q_SIGNALS #define signals Q_SIGNALS
#endif #endif
#include <private/qquickitem_p.h>
#include <private/qquickshape_p.h> #include <private/qquickshape_p.h>
#include <private/qquickshape_p_p.h> #include <private/qquickshape_p_p.h>

View File

@ -10,7 +10,6 @@
#endif #endif
#include <QskPaintedNode.h> #include <QskPaintedNode.h>
#include <QskBoxFillNode.h>
#include <QskBoxRectangleNode.h> #include <QskBoxRectangleNode.h>
#include <QskGradient.h> #include <QskGradient.h>
#include <QskGradientDirection.h> #include <QskGradientDirection.h>
@ -75,20 +74,20 @@ namespace
switch( nodeType ) switch( nodeType )
{ {
case GradientView::Painted: case GradientView::Painted:
text = "QskPaintedNode"; text = "Raster PaintEngine";
break; break;
case GradientView::BoxRectangle: case GradientView::BoxRectangle:
text = "QskBoxRectangleNode"; text = "Colored Vertices";
break; break;
case GradientView::BoxFill: case GradientView::BoxFill:
text = "QskBoxFillNode"; text = "Qskinny Shader";
break; break;
#ifdef SHAPE_GRADIENT #ifdef SHAPE_GRADIENT
case GradientView::Shape: case GradientView::Shape:
text = "QQuickShapeGenericNode"; text = "Qt/Quick Shape Shader";
break; break;
#endif #endif
@ -149,6 +148,11 @@ QSGNode* GradientView::updatePaintNode(
{ {
const QRectF rect( 0, 0, width(), height() ); const QRectF rect( 0, 0, width(), height() );
QskBoxShapeMetrics shape;
#if 0
shape.setRadius( 80 );
#endif
switch( m_nodeType ) switch( m_nodeType )
{ {
case Painted: case Painted:
@ -160,24 +164,17 @@ QSGNode* GradientView::updatePaintNode(
} }
case BoxFill: case BoxFill:
{ {
auto node = gradientNode< QskBoxFillNode >( oldNode ); auto node = gradientNode< QskBoxRectangleNode >( oldNode );
node->updateNode( rect, m_gradient ); node->setHint( QskFillNode::PreferColoredGeometry, false );
node->updateFilling( rect, shape, m_gradient );
return node; return node;
} }
case BoxRectangle: case BoxRectangle:
{ {
QskBoxShapeMetrics shape;
shape.setRadius( 80 );
if ( !QskBoxRenderer::isGradientSupported( shape, m_gradient ) )
{
delete oldNode;
return nullptr;
}
auto node = gradientNode< QskBoxRectangleNode >( oldNode ); auto node = gradientNode< QskBoxRectangleNode >( oldNode );
node->updateNode( rect, shape, m_gradient ); node->setHint( QskFillNode::PreferColoredGeometry, true );
node->updateFilling( rect, shape, m_gradient );
return node; return node;
} }

View File

@ -40,9 +40,9 @@ int main( int argc, char* argv[] )
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
// namespace QtWebEngine doesn't exist in Qt6: https://doc.qt.io/qt-5/qtwebengine.html // namespace QtWebEngine doesn't exist in Qt6: https://doc.qt.io/qt-5/qtwebengine.html
QtWebEngine::initialize(); QtWebEngine::initialize();
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif #endif
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app( argc, argv ); QGuiApplication app( argc, argv );
SkinnyShortcut::enable( SkinnyShortcut::Quit | SkinnyShortcut::DebugShortcuts ); SkinnyShortcut::enable( SkinnyShortcut::Quit | SkinnyShortcut::DebugShortcuts );

View File

@ -107,7 +107,6 @@ list(APPEND HEADERS
nodes/QskBasicLinesNode.h nodes/QskBasicLinesNode.h
nodes/QskBoxNode.h nodes/QskBoxNode.h
nodes/QskBoxClipNode.h nodes/QskBoxClipNode.h
nodes/QskBoxFillNode.h
nodes/QskBoxRectangleNode.h nodes/QskBoxRectangleNode.h
nodes/QskBoxRenderer.h nodes/QskBoxRenderer.h
nodes/QskBoxMetrics.h nodes/QskBoxMetrics.h
@ -149,7 +148,6 @@ list(APPEND SOURCES
nodes/QskBasicLinesNode.cpp nodes/QskBasicLinesNode.cpp
nodes/QskBoxNode.cpp nodes/QskBoxNode.cpp
nodes/QskBoxClipNode.cpp nodes/QskBoxClipNode.cpp
nodes/QskBoxFillNode.cpp
nodes/QskBoxRectangleNode.cpp nodes/QskBoxRectangleNode.cpp
nodes/QskBoxRenderer.cpp nodes/QskBoxRenderer.cpp
nodes/QskBoxMetrics.cpp nodes/QskBoxMetrics.cpp
@ -164,7 +162,6 @@ list(APPEND SOURCES
nodes/QskLinesNode.cpp nodes/QskLinesNode.cpp
nodes/QskPaintedNode.cpp nodes/QskPaintedNode.cpp
nodes/QskPlainTextRenderer.cpp nodes/QskPlainTextRenderer.cpp
nodes/QskRectangleNode.cpp
nodes/QskRichTextRenderer.cpp nodes/QskRichTextRenderer.cpp
nodes/QskSceneTexture.cpp nodes/QskSceneTexture.cpp
nodes/QskSGNode.cpp nodes/QskSGNode.cpp

View File

@ -22,7 +22,6 @@
#include "QskGraphicNode.h" #include "QskGraphicNode.h"
#include "QskGraphic.h" #include "QskGraphic.h"
#include "QskLinesNode.h" #include "QskLinesNode.h"
#include "QskRectangleNode.h"
#include "QskSGNode.h" #include "QskSGNode.h"
#include "QskStippleMetrics.h" #include "QskStippleMetrics.h"
#include "QskTextColors.h" #include "QskTextColors.h"
@ -373,8 +372,9 @@ QSGNode* QskSkinlet::updateBackgroundNode(
if ( !gradient.isValid() ) if ( !gradient.isValid() )
return nullptr; return nullptr;
auto rectNode = QskSGNode::ensureNode< QskRectangleNode >( node ); auto rectNode = QskSGNode::ensureNode< QskBoxRectangleNode >( node );
rectNode->updateNode( rect, gradient ); rectNode->updateFilling( rect, QskBoxShapeMetrics(),
QskBoxBorderMetrics(), gradient );
return rectNode; return rectNode;
} }

View File

@ -14,8 +14,12 @@ QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskTextInput, Panel ) QSK_SUBCONTROL( QskTextInput, Panel )
QSK_SUBCONTROL( QskTextInput, Text ) QSK_SUBCONTROL( QskTextInput, Text )
#if 1
// shouldn't this be a Selected state, TODO ...
QSK_SUBCONTROL( QskTextInput, PanelSelected ) QSK_SUBCONTROL( QskTextInput, PanelSelected )
QSK_SUBCONTROL( QskTextInput, TextSelected ) QSK_SUBCONTROL( QskTextInput, TextSelected )
#endif
QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 )
QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 ) QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 )
@ -458,6 +462,9 @@ void QskTextInput::focusInEvent( QFocusEvent* event )
break; break;
default: default:
#if 1
// auto selecting the complete text ???
#endif
setEditing( true ); setEditing( true );
} }
} }

View File

@ -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() );
}
}

View File

@ -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

View File

@ -4,10 +4,8 @@
*****************************************************************************/ *****************************************************************************/
#include "QskBoxNode.h" #include "QskBoxNode.h"
#include "QskBoxFillNode.h"
#include "QskBoxShadowNode.h" #include "QskBoxShadowNode.h"
#include "QskBoxRectangleNode.h" #include "QskBoxRectangleNode.h"
#include "QskBoxRenderer.h"
#include "QskSGNode.h" #include "QskSGNode.h"
#include "QskGradient.h" #include "QskGradient.h"
@ -67,7 +65,7 @@ void QskBoxNode::updateNode( const QRectF& rect,
QskBoxShadowNode* shadowNode = nullptr; QskBoxShadowNode* shadowNode = nullptr;
QskBoxRectangleNode* rectNode = nullptr; QskBoxRectangleNode* rectNode = nullptr;
QskBoxFillNode* fillNode = nullptr; QskBoxRectangleNode* fillNode = nullptr;
if ( !shadowMetrics.isNull() if ( !shadowMetrics.isNull()
&& shadowColor.isValid() && shadowColor.alpha() != 0 ) && shadowColor.isValid() && shadowColor.alpha() != 0 )
@ -77,29 +75,23 @@ void QskBoxNode::updateNode( const QRectF& rect,
shape, shadowMetrics.blurRadius(), shadowColor ); shape, shadowMetrics.blurRadius(), shadowColor );
} }
/* if ( QskBoxRectangleNode::isCombinedGeometrySupported( gradient ) )
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 ) )
{ {
rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole ); rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole );
rectNode->updateNode( rect, shape, borderMetrics, borderColors, gradient ); rectNode->updateBox( rect, shape, borderMetrics, borderColors, gradient );
} }
else else
{ {
if ( !borderMetrics.isNull() && borderColors.isVisible() ) if ( !borderMetrics.isNull() && borderColors.isVisible() )
{ {
rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole ); rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole );
rectNode->updateNode( rect, shape, borderMetrics, borderColors, QskGradient() ); rectNode->updateBorder( rect, shape, borderMetrics, borderColors );
} }
if ( gradient.isVisible() ) if ( gradient.isVisible() )
{ {
fillNode = qskNode< QskBoxFillNode >( this, FillRole ); fillNode = qskNode< QskBoxRectangleNode >( this, FillRole );
fillNode->updateNode( rect, shape, borderMetrics, gradient ); fillNode->updateFilling( rect, shape, borderMetrics, gradient );
} }
} }

View File

@ -12,46 +12,55 @@
#include "QskGradientDirection.h" #include "QskGradientDirection.h"
#include "QskFillNodePrivate.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 class QskBoxRectangleNodePrivate final : public QskFillNodePrivate
{ {
public: public:
QskHashValue metricsHash = 0; inline void resetNode( QskBoxRectangleNode* node )
QskHashValue colorsHash = 0; {
QRectF rect; 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() QskBoxRectangleNode::QskBoxRectangleNode()
@ -63,132 +72,163 @@ QskBoxRectangleNode::~QskBoxRectangleNode()
{ {
} }
void QskBoxRectangleNode::updateNode( void QskBoxRectangleNode::updateFilling(
const QRectF& rect, const QskGradient& fillGradient ) const QRectF& rect, const QskGradient& gradient )
{ {
updateNode( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), updateFilling( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), gradient );
QskBoxBorderColors(), fillGradient );
} }
void QskBoxRectangleNode::updateNode( const QRectF& rect, void QskBoxRectangleNode::updateFilling( const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskGradient& fillGradient ) const QskBoxShapeMetrics& shape, const QskGradient& gradient )
{ {
updateNode( rect, shape, QskBoxBorderMetrics(), updateFilling( rect, shape, QskBoxBorderMetrics(), gradient );
QskBoxBorderColors(), fillGradient );
} }
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 QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics,
const QskBoxBorderColors& borderColors, const QskGradient& gradient ) const QskBoxBorderColors& borderColors, const QskGradient& gradient )
{ {
Q_D( QskBoxRectangleNode ); 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() ) if ( rect.isEmpty() )
{ {
resetGeometry(); d->resetNode( this );
return; return;
} }
bool hasFill = fillGradient.isVisible(); const bool hasFill = gradient.isVisible();
const bool hasBorder = !borderMetrics.isNull() && borderColors.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;
if ( hasFill && hasBorder ) 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 qWarning() << "QskBoxRenderer does not support radial/conic gradients";
hasBorder = false; fillGradient.setDirection( QskGradient::Linear );
} }
QskBoxRenderer::renderBox( rect, shape, borderMetrics,
borderColors, fillGradient, *geometry() );
markDirty( QSGNode::DirtyGeometry );
} }
} }
else if ( hasFill )
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 ) )
{ {
if ( ( hasFill && isFillMonochrome ) updateFilling( rect, shape, borderMetrics, gradient );
|| ( hasBorder && !isBorderMonochrome )
{
coloring = QskFillNode::Monochrome;
}
} }
#endif else if ( hasBorder )
auto& geometry = *this->geometry();
if ( coloring == QskFillNode::Polychrome )
{ {
setColoring( coloring ); updateBorder( rect, shape, borderMetrics, borderColors );
QskBoxRenderer::renderBox( d->rect, shape, borderMetrics,
borderColors, fillGradient, geometry );
} }
else else
{ {
if ( hasFill ) d->resetNode( this );
{
setColoring( fillGradient.rgbStart() );
QskBoxRenderer::renderFillGeometry(
d->rect, shape, QskBoxBorderMetrics(), geometry );
}
else
{
setColoring( borderColors.left().rgbStart() );
QskBoxRenderer::renderBorderGeometry(
d->rect, shape, borderMetrics, geometry );
}
} }
}
geometry.markVertexDataDirty();
bool QskBoxRectangleNode::isCombinedGeometrySupported( const QskGradient& gradient )
{
return QskBoxRenderer::isGradientSupported( gradient );
} }

View File

@ -24,15 +24,29 @@ class QSK_EXPORT QskBoxRectangleNode : public QskFillNode
QskBoxRectangleNode(); QskBoxRectangleNode();
~QskBoxRectangleNode() override; ~QskBoxRectangleNode() override;
void updateNode( const QRectF&, void updateBox( const QRectF&,
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&,
const QskBoxBorderColors&, const QskGradient& ); 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& ); 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: private:
Q_DECLARE_PRIVATE( QskBoxRectangleNode ) Q_DECLARE_PRIVATE( QskBoxRectangleNode )
}; };

View File

@ -34,6 +34,9 @@ static inline QskVertex::ColoredLine* qskAllocateColoredLines(
static inline QskGradient qskEffectiveGradient( static inline QskGradient qskEffectiveGradient(
const QRectF& rect, const QskGradient& gradient ) const QRectF& rect, const QskGradient& gradient )
{ {
if ( !gradient.isVisible() )
return gradient;
const auto dir = gradient.linearDirection(); const auto dir = gradient.linearDirection();
auto g = gradient; auto g = gradient;
@ -67,8 +70,7 @@ static inline bool qskMaybeSpreading( const QskGradient& gradient )
return true; return true;
} }
bool QskBoxRenderer::isGradientSupported( bool QskBoxRenderer::isGradientSupported( const QskGradient& gradient )
const QskBoxShapeMetrics&, const QskGradient& gradient )
{ {
if ( !gradient.isVisible() || gradient.isMonochrome() ) if ( !gradient.isVisible() || gradient.isMonochrome() )
return true; return true;
@ -112,6 +114,7 @@ void QskBoxRenderer::renderBorderGeometry(
const QskBoxBorderMetrics& border, QSGGeometry& geometry ) const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{ {
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
geometry.markVertexDataDirty();
const QskBoxMetrics metrics( rect, shape, border ); const QskBoxMetrics metrics( rect, shape, border );
const QskBoxBasicStroker stroker( metrics ); const QskBoxBasicStroker stroker( metrics );
@ -132,6 +135,7 @@ void QskBoxRenderer::renderFillGeometry(
const QskBoxBorderMetrics& border, QSGGeometry& geometry ) const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{ {
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
geometry.markVertexDataDirty();
const QskBoxMetrics metrics( rect, shape, border ); const QskBoxMetrics metrics( rect, shape, border );
QskBoxBasicStroker stroker( metrics ); QskBoxBasicStroker stroker( metrics );
@ -154,6 +158,7 @@ void QskBoxRenderer::renderBox( const QRectF& rect,
QSGGeometry& geometry ) QSGGeometry& geometry )
{ {
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
geometry.markVertexDataDirty();
const QskBoxMetrics metrics( rect, shape, border ); const QskBoxMetrics metrics( rect, shape, border );
const auto effectiveGradient = qskEffectiveGradient( metrics.innerRect, gradient ); const auto effectiveGradient = qskEffectiveGradient( metrics.innerRect, gradient );

View File

@ -39,7 +39,7 @@ namespace QskBoxRenderer
Filling the geometry usually with color information: Filling the geometry usually with color information:
see QSGGeometry::defaultAttributes_ColoredPoint2D() see QSGGeometry::defaultAttributes_ColoredPoint2D()
*/ */
QSK_EXPORT bool isGradientSupported( const QskBoxShapeMetrics&, const QskGradient& ); QSK_EXPORT bool isGradientSupported( const QskGradient& );
QSK_EXPORT void renderBox( const QRectF&, QSK_EXPORT void renderBox( const QRectF&,
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&,

View File

@ -115,7 +115,7 @@ void QskFillNode::setColoring( Coloring coloring )
QskFillNode::Coloring QskFillNode::coloring() const QskFillNode::Coloring QskFillNode::coloring() const
{ {
return d_func()->coloring; return static_cast< QskFillNode::Coloring >( d_func()->coloring );
} }
void QskFillNode::setColoring( const QColor& color ) 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 bool QskFillNode::isGeometryColored() const
{ {
return d_func()->geometry.attributeCount() != 1; return d_func()->geometry.attributeCount() != 1;
} }
#include "moc_QskFillNode.cpp"

View File

@ -8,12 +8,15 @@
#include "QskGlobal.h" #include "QskGlobal.h"
#include <qsgnode.h> #include <qsgnode.h>
#include <qcolor.h>
class QskFillNodePrivate; class QskFillNodePrivate;
class QskGradient; class QskGradient;
class QSK_EXPORT QskFillNode : public QSGGeometryNode class QSK_EXPORT QskFillNode : public QSGGeometryNode
{ {
Q_GADGET
using Inherited = QSGGeometryNode; using Inherited = QSGGeometryNode;
public: public:
@ -27,6 +30,28 @@ class QSK_EXPORT QskFillNode : public QSGGeometryNode
Conic 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();
~QskFillNode() override; ~QskFillNode() override;
@ -35,11 +60,20 @@ class QSK_EXPORT QskFillNode : public QSGGeometryNode
void setColoring( Coloring ); void setColoring( Coloring );
Coloring coloring() const; Coloring coloring() const;
void setColoring( QRgb );
void setColoring( const QColor& ); void setColoring( const QColor& );
void setColoring( Qt::GlobalColor );
void setColoring( const QRectF&, const QskGradient& ); void setColoring( const QRectF&, const QskGradient& );
bool isGeometryColored() const; bool isGeometryColored() const;
void setHints( Hints );
Hints hints() const;
void setHint( Hint, bool on = true );
bool hasHint( Hint ) const;
protected: protected:
QskFillNode( QskFillNodePrivate& ); QskFillNode( QskFillNodePrivate& );
@ -47,4 +81,14 @@ class QSK_EXPORT QskFillNode : public QSGGeometryNode
Q_DECLARE_PRIVATE( QskFillNode ) 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 #endif

View File

@ -19,6 +19,8 @@ class QskFillNodePrivate : public QSGGeometryNodePrivate
public: public:
inline QskFillNodePrivate() inline QskFillNodePrivate()
: geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 )
, coloring( QskFillNode::Polychrome )
, hints( QskFillNode::PreferColoredGeometry )
{ {
} }
@ -26,7 +28,9 @@ class QskFillNodePrivate : public QSGGeometryNodePrivate
friend class QskFillNode; friend class QskFillNode;
QSGGeometry geometry; QSGGeometry geometry;
QskFillNode::Coloring coloring = QskFillNode::Polychrome;
uint coloring : 5;
uint hints : 3;
}; };
#endif #endif

View File

@ -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 );
}
}
}

View File

@ -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