radial tickmarks node initial commit

This commit is contained in:
Vogel, Rick 2023-08-08 15:06:09 +02:00
parent 3fee4907c0
commit 8f6b83be8e
7 changed files with 389 additions and 0 deletions

View File

@ -6,6 +6,7 @@ add_subdirectory(invoker)
add_subdirectory(shadows) add_subdirectory(shadows)
add_subdirectory(shapes) add_subdirectory(shapes)
add_subdirectory(charts) add_subdirectory(charts)
add_subdirectory(nodes)
if (BUILD_INPUTCONTEXT) if (BUILD_INPUTCONTEXT)
add_subdirectory(inputpanel) add_subdirectory(inputpanel)

View File

@ -0,0 +1,12 @@
############################################################################
# QSkinny - Copyright (C) 2016 Uwe Rathmann
# SPDX-License-Identifier: BSD-3-Clause
############################################################################
qsk_add_example(nodes
main.cpp
RadialNodes.h
RadialNodes.cpp
RadialNodesSkinlet.h
RadialNodesSkinlet.cpp
)

View File

@ -0,0 +1,17 @@
#include "RadialNodes.h"
QSK_SUBCONTROL(RadialNodes, Foreground)
QSK_SUBCONTROL(RadialNodes, Text)
QSK_SUBCONTROL(RadialNodes, Lines)
RadialNodes::RadialNodes( QQuickItem* const parent ) : QskControl( parent )
{
// TODO move into your skin
setColor(Background, Qt::lightGray);
setColor(Foreground, Qt::black);
setColor(Text, Qt::white);
setColor(Lines, Qt::red);
setStrutSizeHint( Lines, 2, 10 );
}
#include "moc_RadialNodes.cpp"

View File

@ -0,0 +1,10 @@
#pragma once
#include <QskControl.h>
class RadialNodes : public QskControl
{
public:
QSK_SUBCONTROLS(Foreground, Text, Lines)
explicit RadialNodes( QQuickItem* parent = nullptr );
};

View File

@ -0,0 +1,244 @@
#include "RadialNodesSkinlet.h"
#include "RadialNodes.h"
#include <QskFunctions.h>
#include <QskSGNode.h>
#include <QskScaleTickmarks.h>
#include <QSGFlatColorMaterial>
#include <QSGGeometryNode>
#include <qmath.h>
namespace
{
template< typename T >
Q_REQUIRED_RESULT inline bool compareExchange( T& dst, const T& src )
{
if ( dst != src )
{
dst = src;
return true;
}
return false;
}
template<>
Q_REQUIRED_RESULT inline bool compareExchange< float >( float& dst, const float& src )
{
if ( !qskFuzzyCompare( dst, src ) )
{
dst = src;
return true;
}
return false;
}
template<>
Q_REQUIRED_RESULT inline bool compareExchange< qreal >( qreal& dst, const qreal& src )
{
if ( !qskFuzzyCompare( dst, src ) )
{
dst = src;
return true;
}
return false;
}
class QskRadialTickmarksNode final : public QSGGeometryNode
{
public:
QskRadialTickmarksNode()
: m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
{
m_geometry.setDrawingMode( QSGGeometry::DrawLines );
m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern );
setGeometry( &m_geometry );
setMaterial( &m_material );
}
void setMaterialProperties( const QColor& color )
{
if ( m_material.color() != color )
{
m_material.setColor( color );
markDirty( QSGNode::DirtyMaterial );
}
}
void setGeometryProperties( const QskScaleTickmarks& tickmarks,
const QVector2D& r1 = { 1.0f, 1.0f }, const QVector3D& height = { 1.0f, 1.0f, 1.0f },
const QVector2D& center = {}, Qt::Alignment alignment = Qt::AlignVCenter,
float lineWidth = 1.0 )
{
auto dirty = false;
if ( dirty |= ( m_geometry.lineWidth() != lineWidth ) )
{
m_geometry.setLineWidth( lineWidth );
}
dirty |= compareExchange( m_radius, r1 );
dirty |= compareExchange( m_height, height );
dirty |= compareExchange( m_center, center );
dirty |= compareExchange( m_alignment, alignment );
dirty |= compareExchange( m_tickmarksHash, tickmarks.hash() );
if ( dirty )
{
update( tickmarks );
}
}
private:
void update( const QskScaleTickmarks& tickmarks )
{
using T = QskScaleTickmarks::TickType;
if ( m_geometry.vertexCount() != tickmarks.tickCount() )
{
m_geometry.allocate( tickmarks.tickCount() * 2 );
}
QVector2D r1[ 3 ];
QVector2D r2[ 3 ];
if ( m_alignment.testFlag( Qt::AlignBottom ) || m_alignment.testFlag( Qt::AlignLeft ))
{
for ( int i : { T::MinorTick, T::MediumTick, T::MajorTick } )
{
r1[ i ] = m_radius;
r2[ i ] = m_radius.normalized() * ( m_radius.length() + m_height[ i ] );
}
}
else if ( m_alignment.testFlag( Qt::AlignTop ) || m_alignment.testFlag( Qt::AlignRight ))
{
for ( int i : { T::MinorTick, T::MediumTick, T::MajorTick } )
{
r1[ i ] = m_radius.normalized() * ( m_radius.length() - m_height[ i ] );
r2[ i ] = m_radius;
}
}
else
{
for ( int i : { T::MinorTick, T::MediumTick, T::MajorTick } )
{
r1[ i ] = m_radius.normalized() * ( m_radius.length() - m_height[ i ] / 2 );
r2[ i ] = m_radius.normalized() * ( m_radius.length() + m_height[ i ] / 2 );
}
}
auto* vertexData = m_geometry.vertexDataAsPoint2D();
for ( auto type : { T::MinorTick, T::MediumTick, T::MajorTick } )
{
for ( const auto tick : tickmarks.ticks( type ) )
{
const auto i = static_cast< int >( type );
const auto rad = qDegreesToRadians( tick );
const auto p1 =
r1[ i ] * QVector2D{ ( float ) qFastCos( rad ), ( float ) qFastSin( rad ) };
const auto p2 =
r2[ i ] * QVector2D{ ( float ) qFastCos( rad ), ( float ) qFastSin( rad ) };
vertexData[ 0 ].set( p1.x() + m_center.x(), p1.y() + m_center.y() );
vertexData[ 1 ].set( p2.x() + m_center.x(), p2.y() + m_center.y() );
vertexData += 2;
}
}
m_geometry.markVertexDataDirty();
markDirty( QSGNode::DirtyGeometry );
}
QSGGeometry m_geometry;
QSGFlatColorMaterial m_material;
QVector2D m_radius = { 1.0f, 1.0f };
QVector2D m_center = { 0.0f, 0.0f };
QVector3D m_height = { 1.0f, 1.0f, 1.0f };
Qt::Alignment m_alignment = Qt::AlignVCenter;
QskHashValue m_tickmarksHash{ 0 };
};
}
RadialNodesSkinlet::RadialNodesSkinlet( QskSkin* const skin )
: QskSkinlet( skin )
{
setNodeRoles( { Lines } );
}
QRectF RadialNodesSkinlet::subControlRect( const QskSkinnable* const skinnable,
const QRectF& contentsRect, const QskAspect::Subcontrol subControl ) const
{
if ( subControl == RadialNodes::Text )
{
return contentsRect.adjusted( +20, +20, -20, -20 );
}
else if ( subControl == RadialNodes::Foreground )
{
return contentsRect.adjusted( +10, +10, -10, -10 );
}
else if ( subControl == RadialNodes::Lines )
{
return contentsRect;
}
return QskSkinlet::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* RadialNodesSkinlet::updateSubNode(
const QskSkinnable* const skinnable, const quint8 nodeRole, QSGNode* const node ) const
{
using Q = RadialNodes;
switch ( static_cast< NodeRole >( nodeRole ) )
{
case Text:
return updateTextNode( skinnable, node, "RadialNodes", RadialNodes::Text );
case Foreground:
return updateBoxNode( skinnable, node, RadialNodes::Foreground );
case Lines: {
const auto* const control = static_cast< const Q* >( skinnable );
QskScaleTickmarks tickmarks;
QVector< qreal > major, medium, minor;
for ( int deg = 0; deg < 360; deg += 1 )
{
if ( deg % 10 == 0 )
major << deg;
else if ( deg % 5 == 0 )
medium << deg;
else
minor << deg;
}
tickmarks.setMajorTicks( major );
tickmarks.setMediumTicks( medium );
tickmarks.setMinorTicks( minor );
const auto subControlRect = control->subControlContentsRect( Q::Lines );
const auto alignment = control->alignmentHint( Q::Lines );
const auto size = control->strutSizeHint( Q::Lines );
const auto rX = static_cast< float >( subControlRect.width() / 2 );
const auto rY = static_cast< float >( subControlRect.height() / 2 );
const auto radius = QVector2D{ rX, rY };
const auto height =
QVector3D{
( float ) size.height() * 0.50f,
( float ) size.height() * 0.75f, ( float ) size.height() * 1.0f };
const auto c = QVector2D{ ( float ) subControlRect.center().x(),
( float ) subControlRect.center().y() };
auto* const root = QskSGNode::ensureNode< QskRadialTickmarksNode >( node );
root->setMaterialProperties( skinnable->color( Q::Lines ) );
root->setGeometryProperties( tickmarks, radius, height, c, alignment, size.width() );
return root;
}
default:
return QskSkinlet::updateSubNode( skinnable, nodeRole, node );
}
}
#include "moc_RadialNodesSkinlet.cpp"

View File

@ -0,0 +1,22 @@
#pragma once
#include <QskSkinlet.h>
class RadialNodesSkinlet : public QskSkinlet
{
Q_GADGET
public:
enum NodeRole
{
Text,
Foreground,
Lines,
RoleCount
};
Q_INVOKABLE RadialNodesSkinlet( QskSkin* skin = nullptr );
protected:
Q_REQUIRED_RESULT QRectF subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl) const override;
Q_REQUIRED_RESULT QSGNode* updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node) const override;
};

83
playground/nodes/main.cpp Normal file
View File

@ -0,0 +1,83 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include <SkinnyShapeProvider.h>
#include <SkinnyShortcut.h>
#include <QskWindow.h>
#include <QskObjectCounter.h>
#include <QskFocusIndicator.h>
#include <QskBox.h>
#include <QskLinearBox.h>
#include <QskSkinManager.h>
#include <QskAnimationHint.h>
#include <QskSkinTransition.h>
#include <QskSetup.h>
#include <QskSkin.h>
#include <QskSegmentedBar.h>
#include <QskSlider.h>
#include <QskTextLabel.h>
#include <QGuiApplication>
#include "RadialNodes.h"
#include "RadialNodesSkinlet.h"
int main( int argc, char* argv[] )
{
#ifdef ITEM_STATISTICS
QskObjectCounter counter( true );
#endif
QGuiApplication app( argc, argv );
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
qskSkinManager->setPluginPaths(
{ R"(C:\repositories\qskinny_rveh-install-win\plugins)" } );
qskSetup->setItemUpdateFlag( QskQuickItem::PreferRasterForTextures, true );
QskWindow window;
window.resize( 800, 600 );
auto* const layout = new QskLinearBox(Qt::Horizontal, window.contentItem() );
auto* const left = new QskLinearBox(Qt::Vertical, layout);
auto* const right = new QskLinearBox(Qt::Vertical, layout);
auto* const control = new RadialNodes( left );
auto* const skinlet = new RadialNodesSkinlet;
{
(void) new QskTextLabel("Tickmark Alignment", right);
auto* const alignment = new QskSegmentedBar(right);
alignment->setOptions( { "Center", "Bottom", "Top" } );
QObject::connect(alignment, &QskSegmentedBar::selectedIndexChanged, control, [=](const int i){
static const Qt::Alignment a[ 3 ]{ Qt::AlignVCenter, Qt::AlignBottom,
Qt::AlignTop };
control->setAlignmentHint(RadialNodes::Lines, a[i]);
});
(void) new QskTextLabel("Tickmark Size", right);
auto* const sliderW = new QskSlider(right);
auto* const sliderH = new QskSlider(right);
sliderW->setMinimum(1.0);
sliderW->setMaximum(4.0);
sliderW->setValue(1.0);
sliderH->setValue( 0.5 );
auto updateStrutSizeHint = [=](const qreal){
const auto width = sliderW->value();
const auto height = sliderH->value() * control->height();
control->setStrutSizeHint(RadialNodes::Lines, width, height );
};
QObject::connect( sliderW, &QskSlider::valueChanged, control, updateStrutSizeHint );
QObject::connect( sliderH, &QskSlider::valueChanged, control, updateStrutSizeHint );
}
control->setSkinlet(skinlet);
skinlet->setOwnedBySkinnable( true );
window.show();
return app.exec();
}