6#include "QskGraduationRenderer.h"
7#include "QskGraduationMetrics.h"
8#include "QskTickmarks.h"
11#include "QskGraduationNode.h"
12#include "QskTextOptions.h"
13#include "QskTextColors.h"
14#include "QskGraphic.h"
15#include "QskColorFilter.h"
16#include "QskControl.h"
17#include "QskIntervalF.h"
18#include "QskFunctions.h"
21#include <qfontmetrics.h>
22#include <qquickwindow.h>
29 inline ScaleMap(
bool isHorizontal,
const QTransform& transform )
30 : t( isHorizontal ? transform.dx() : transform.dy() )
31 , f( isHorizontal ? transform.m11() : transform.m22() )
35 inline qreal map( qreal v )
const {
return t + f * v; };
43static inline bool qskIsHorizontal( Qt::Edge edge )
45 return edge & ( Qt::TopEdge | Qt::BottomEdge );
48static QSGNode* qskRemoveTraillingNodes( QSGNode* node, QSGNode* childNode )
50 QskSGNode::removeAllChildNodesFrom( node, childNode );
54static inline QTransform qskScaleTransform( Qt::Edge edge,
59 if ( qskIsHorizontal( edge ) )
61 auto transform = T::fromTranslate( -boundaries.lowerBound(), 0.0 );
62 transform *= T::fromScale( range.length() / boundaries.length(), 1.0 );
63 transform *= T::fromTranslate( range.lowerBound(), 0.0 );
69 auto transform = T::fromTranslate( 0.0, -boundaries.lowerBound() );
70 transform *= T::fromScale( 1.0, -range.length() / boundaries.length() );
71 transform *= T::fromTranslate( 0.0, range.upperBound() );
77static inline quint8 qskLabelNodeRole(
const QVariant& label )
79 if ( !label.isNull() )
81 if ( label.canConvert< QString >() )
88 return QskSGNode::NoRole;
91class QskGraduationRenderer::PrivateData
104 qreal position = 0.0;
108 QColor tickColor = Qt::black;
119 Qt::Edge edge = Qt::BottomEdge;
120 QskGraduationRenderer::Flags flags = ClampedLabels;
123QskGraduationRenderer::QskGraduationRenderer()
124 : m_data( new PrivateData() )
128QskGraduationRenderer::~QskGraduationRenderer()
132void QskGraduationRenderer::setEdge( Qt::Edge edge )
137Qt::Edge QskGraduationRenderer::edge()
const
142void QskGraduationRenderer::setFlag( Flag flag,
bool on )
145 m_data->flags |= flag;
147 m_data->flags &= ~flag;
150void QskGraduationRenderer::setFlags( Flags flags )
152 m_data->flags = flags;
155QskGraduationRenderer::Flags QskGraduationRenderer::flags()
const
157 return m_data->flags;
160void QskGraduationRenderer::setBoundaries( qreal lowerBound, qreal upperBound )
162 setBoundaries(
QskIntervalF( lowerBound, upperBound ) );
165void QskGraduationRenderer::setBoundaries(
const QskIntervalF& boundaries )
167 m_data->boundaries = boundaries;
172 return m_data->boundaries;
175qreal QskGraduationRenderer::position()
const
177 return m_data->position;
180void QskGraduationRenderer::setPosition( qreal pos )
182 m_data->position = pos;
185void QskGraduationRenderer::setRange( qreal from, qreal to )
190void QskGraduationRenderer::setRange(
const QskIntervalF& range )
192 m_data->range = range;
197 return m_data->range;
200void QskGraduationRenderer::setTickmarks(
const QskTickmarks& tickmarks )
202 m_data->tickmarks = tickmarks;
205const QskTickmarks& QskGraduationRenderer::tickmarks()
const
207 return m_data->tickmarks;
210void QskGraduationRenderer::setSpacing( qreal spacing )
212 m_data->spacing = qMax( spacing, 0.0 );
215qreal QskGraduationRenderer::spacing()
const
217 return m_data->spacing;
220void QskGraduationRenderer::setTickColor(
const QColor& color )
222 m_data->tickColor = color;
225QColor QskGraduationRenderer::tickColor()
const
227 return m_data->tickColor;
232 m_data->metrics = metrics;
237 return m_data->metrics;
240void QskGraduationRenderer::setFont(
const QFont& font )
245QFont QskGraduationRenderer::font()
const
250void QskGraduationRenderer::setTextColors(
const QskTextColors& textColors )
252 m_data->textColors = textColors;
257 return m_data->textColors;
260void QskGraduationRenderer::setColorFilter(
const QskColorFilter& colorFilter )
262 m_data->colorFilter = colorFilter;
267 return m_data->colorFilter;
270QSGNode* QskGraduationRenderer::updateNode(
273 enum Role : quint8 { Ticks = 1, Labels = 2 };
274 static const QVector< quint8 > roles = { Ticks, Labels };
276 const auto transform = qskScaleTransform(
277 m_data->edge, m_data->boundaries, m_data->range );
279 if ( node ==
nullptr )
280 node =
new QSGNode();
282 for (
auto role : roles )
284 auto oldNode = QskSGNode::findChildNode( node, role );
286 auto newNode = ( role == Ticks )
287 ? updateTicksNode( transform, oldNode )
288 : updateLabelsNode( skinnable, transform, oldNode );
290 QskSGNode::replaceChildNode( roles, role, node, oldNode, newNode );
296QSGNode* QskGraduationRenderer::updateTicksNode(
297 const QTransform& transform, QSGNode* node )
const
300 if ( m_data->flags & Backbone )
301 backbone = m_data->boundaries;
303 const auto orientation = qskIsHorizontal( m_data->edge )
304 ? Qt::Horizontal : Qt::Vertical;
306 auto alignment = QskGraduationNode::Centered;
308 if ( !( m_data->flags & CenteredTickmarks ) )
310 switch( m_data->edge )
314 alignment = QskGraduationNode::Leading;
318 alignment = QskGraduationNode::Trailing;
323 auto graduationNode = QskSGNode::ensureNode< QskGraduationNode >( node );
325 graduationNode->setColor( m_data->tickColor );
326 graduationNode->setAxis( orientation, m_data->position, transform );
327 graduationNode->setTickMetrics( alignment, m_data->metrics );
328 graduationNode->setPixelAlignment( Qt::Horizontal | Qt::Vertical );
330 graduationNode->update( m_data->tickmarks, backbone );
332 return graduationNode;
335QSGNode* QskGraduationRenderer::updateLabelsNode(
const QskSkinnable* skinnable,
336 const QTransform& transform, QSGNode* node )
const
338 const auto ticks = m_data->tickmarks.majorTicks();
339 if ( ticks.isEmpty() )
342 if( node ==
nullptr )
345 const QFontMetricsF fm( m_data->font );
347 auto nextNode = node->firstChild();
351 for (
auto tick : ticks )
353 const auto label = labelAt( tick );
355 const auto role = qskLabelNodeRole( label );
357 if ( nextNode && QskSGNode::nodeRole( nextNode ) != role )
358 nextNode = qskRemoveTraillingNodes( node, nextNode );
362 if ( label.canConvert< QString >() )
364 size = qskTextRenderSize( fm, label.toString() );
368 const auto graphic = label.value<
QskGraphic >();
369 if ( !graphic.isNull() )
371 size.rheight() = fm.height();
372 size.rwidth() = graphic.widthForHeight( size.height() );
376 if ( size.isEmpty() )
379 const auto rect = labelRect( transform, tick, size );
381 if ( !lastRect.isEmpty() && lastRect.intersects( rect ) )
388 if ( tick != ticks.last() )
391 if (
auto obsoleteNode = nextNode
392 ? nextNode->previousSibling() : node->lastChild() )
394 node->removeChildNode( obsoleteNode );
395 if ( obsoleteNode->flags() & QSGNode::OwnedByParent )
400 nextNode = updateTickLabelNode( skinnable, nextNode, label, rect );
406 if ( nextNode->parent() != node )
408 QskSGNode::setNodeRole( nextNode, role );
409 node->appendChildNode( nextNode );
412 nextNode = nextNode->nextSibling();
416 qskRemoveTraillingNodes( node, nextNode );
421QVariant QskGraduationRenderer::labelAt( qreal pos )
const
423 return QString::number( pos,
'g' );
427QSizeF QskGraduationRenderer::boundingLabelSize()
const
429 QSizeF boundingSize( 0.0, 0.0 );
431 const auto ticks = m_data->tickmarks.majorTicks();
432 if ( ticks.isEmpty() )
435 const QFontMetricsF fm( m_data->font );
437 const qreal h = fm.height();
439 for (
auto tick : ticks )
441 const auto label = labelAt( tick );
442 if ( label.isNull() )
445 if ( label.canConvert< QString >() )
447 boundingSize = boundingSize.expandedTo(
448 qskTextRenderSize( fm, label.toString() ) );
452 const auto graphic = label.value<
QskGraphic >();
453 if ( !graphic.isNull() )
455 const auto w = graphic.widthForHeight( h );
456 boundingSize.setWidth( qMax( boundingSize.width(), w ) );
464QRectF QskGraduationRenderer::labelRect(
465 const QTransform& transform, qreal tick,
const QSizeF& labelSize )
const
467 const auto isHorizontal = qskIsHorizontal( m_data->edge );
469 const auto tickLength = m_data->metrics.maxLength();
471 auto offset = tickLength + m_data->spacing;
472 if ( m_data->flags & CenteredTickmarks )
473 offset -= 0.5 * tickLength;
475 const bool clampLabels = m_data->flags & ClampedLabels;
477 const qreal w = labelSize.width();
478 const qreal h = labelSize.height();
482 const ScaleMap map( isHorizontal, transform );
484 const auto tickPos = map.map( tick );
489 min = map.map( m_data->boundaries.lowerBound() );
490 max = map.map( m_data->boundaries.upperBound() );
495 x = tickPos - 0.5 * w;
498 x = qBound( min, x, max - w );
500 y = m_data->position + offset;
504 const auto tickPos = map.map( tick );
505 y = tickPos - 0.5 * h;
508 y = qBound( max, y, min - h );
510 x = m_data->position - offset - w;
513 return QRectF( x, y, w, h );
516QSGNode* QskGraduationRenderer::updateTickLabelNode(
const QskSkinnable* skinnable,
517 QSGNode* node,
const QVariant& label,
const QRectF& rect )
const
519 if ( label.canConvert< QString >() )
521 return QskSkinlet::updateTextNode( skinnable, node,
522 rect, Qt::AlignCenter, label.toString(), m_data->font,
528 const auto alignment = qskIsHorizontal( m_data->edge )
529 ? ( Qt::AlignHCenter | Qt::AlignBottom )
530 : ( Qt::AlignRight | Qt::AlignVCenter );
532 return QskSkinlet::updateGraphicNode(
534 m_data->colorFilter, rect, alignment );
540#include "moc_QskGraduationRenderer.cpp"
A paint device for scalable graphics.