2020-11-22 15:28:56 +01:00
|
|
|
/******************************************************************************
|
2024-01-17 14:31:45 +01:00
|
|
|
* QSkinny - Copyright (C) The authors
|
2023-04-06 09:23:37 +02:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2020-11-22 15:28:56 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
#include "QskGraduationRenderer.h"
|
2023-11-28 14:35:28 +01:00
|
|
|
#include "QskGraduationMetrics.h"
|
2023-11-28 10:46:03 +01:00
|
|
|
#include "QskTickmarks.h"
|
2020-11-22 15:28:56 +01:00
|
|
|
#include "QskSkinlet.h"
|
|
|
|
#include "QskSGNode.h"
|
2023-11-28 14:35:28 +01:00
|
|
|
#include "QskGraduationNode.h"
|
2020-11-22 15:28:56 +01:00
|
|
|
#include "QskTextOptions.h"
|
2022-03-24 11:14:46 +01:00
|
|
|
#include "QskTextColors.h"
|
2020-11-22 15:28:56 +01:00
|
|
|
#include "QskGraphic.h"
|
2022-03-24 11:14:46 +01:00
|
|
|
#include "QskColorFilter.h"
|
2020-11-22 15:28:56 +01:00
|
|
|
#include "QskControl.h"
|
2022-03-24 11:14:46 +01:00
|
|
|
#include "QskIntervalF.h"
|
2020-11-22 15:28:56 +01:00
|
|
|
#include "QskFunctions.h"
|
|
|
|
|
|
|
|
#include <qstring.h>
|
|
|
|
#include <qfontmetrics.h>
|
2023-11-28 13:36:47 +01:00
|
|
|
#include <qquickwindow.h>
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class ScaleMap
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline ScaleMap( bool isHorizontal, const QTransform& transform )
|
|
|
|
: t( isHorizontal ? transform.dx() : transform.dy() )
|
|
|
|
, f( isHorizontal ? transform.m11() : transform.m22() )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2025-01-03 10:08:10 +01:00
|
|
|
inline qreal map( qreal v ) const { return t + f * v; }
|
2023-11-28 13:36:47 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
const qreal t;
|
|
|
|
const qreal f;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool qskIsHorizontal( Qt::Edge edge )
|
|
|
|
{
|
|
|
|
return edge & ( Qt::TopEdge | Qt::BottomEdge );
|
|
|
|
}
|
2020-11-22 15:28:56 +01:00
|
|
|
|
|
|
|
static QSGNode* qskRemoveTraillingNodes( QSGNode* node, QSGNode* childNode )
|
|
|
|
{
|
|
|
|
QskSGNode::removeAllChildNodesFrom( node, childNode );
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
static inline QTransform qskScaleTransform( Qt::Edge edge,
|
|
|
|
const QskIntervalF& boundaries, const QskIntervalF& range )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
using T = QTransform;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( qskIsHorizontal( edge ) )
|
2020-12-05 15:09:31 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
auto transform = T::fromTranslate( -boundaries.lowerBound(), 0.0 );
|
|
|
|
transform *= T::fromScale( range.length() / boundaries.length(), 1.0 );
|
|
|
|
transform *= T::fromTranslate( range.lowerBound(), 0.0 );
|
|
|
|
|
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto transform = T::fromTranslate( 0.0, -boundaries.lowerBound() );
|
|
|
|
transform *= T::fromScale( 1.0, -range.length() / boundaries.length() );
|
|
|
|
transform *= T::fromTranslate( 0.0, range.upperBound() );
|
|
|
|
|
|
|
|
return transform;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
2023-11-28 13:36:47 +01:00
|
|
|
}
|
2020-12-05 15:09:31 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
static inline quint8 qskLabelNodeRole( const QVariant& label )
|
|
|
|
{
|
|
|
|
if ( !label.isNull() )
|
2020-12-05 15:09:31 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( label.canConvert< QString >() )
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if ( label.canConvert< QskGraphic >() )
|
|
|
|
return 2;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
2023-11-28 13:36:47 +01:00
|
|
|
|
|
|
|
return QskSGNode::NoRole;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
class QskGraduationRenderer::PrivateData
|
2022-03-24 11:14:46 +01:00
|
|
|
{
|
|
|
|
public:
|
2023-11-28 13:36:47 +01:00
|
|
|
|
|
|
|
// Coordinates related to the scales
|
2022-03-24 11:14:46 +01:00
|
|
|
QskIntervalF boundaries;
|
2023-11-28 10:46:03 +01:00
|
|
|
QskTickmarks tickmarks;
|
2022-03-24 11:14:46 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
/*
|
|
|
|
Item cooordinates. In case of an horizontal scale
|
|
|
|
position is an y coordinate, while range corresponds to x coordinates
|
|
|
|
( vertical: v.v )
|
|
|
|
*/
|
|
|
|
qreal position = 0.0;
|
|
|
|
QskIntervalF range;
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
QColor tickColor = Qt::black; // rgb value ???
|
|
|
|
#endif
|
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
QskGraduationMetrics metrics = { 4, 6, 8 };
|
2023-11-28 13:36:47 +01:00
|
|
|
qreal spacing = 5.0;
|
2022-03-24 11:14:46 +01:00
|
|
|
|
|
|
|
QFont font;
|
|
|
|
QskTextColors textColors;
|
|
|
|
|
|
|
|
QskColorFilter colorFilter;
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
Qt::Edge edge = Qt::BottomEdge;
|
2023-11-28 13:45:14 +01:00
|
|
|
QskGraduationRenderer::Flags flags = ClampedLabels;
|
2022-03-24 11:14:46 +01:00
|
|
|
};
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QskGraduationRenderer::QskGraduationRenderer()
|
2022-03-24 11:14:46 +01:00
|
|
|
: m_data( new PrivateData() )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QskGraduationRenderer::~QskGraduationRenderer()
|
2022-03-24 11:14:46 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setEdge( Qt::Edge edge )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
m_data->edge = edge;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
Qt::Edge QskGraduationRenderer::edge() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
return m_data->edge;
|
2023-04-24 11:55:36 +02:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setFlag( Flag flag, bool on )
|
2023-02-28 11:59:46 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( on )
|
|
|
|
m_data->flags |= flag;
|
|
|
|
else
|
|
|
|
m_data->flags &= ~flag;
|
2023-02-28 11:59:46 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setFlags( Flags flags )
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
m_data->flags = flags;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QskGraduationRenderer::Flags QskGraduationRenderer::flags() const
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
return m_data->flags;
|
2023-04-24 11:55:36 +02:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setBoundaries( qreal lowerBound, qreal upperBound )
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
setBoundaries( QskIntervalF( lowerBound, upperBound ) );
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setBoundaries( const QskIntervalF& boundaries )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
m_data->boundaries = boundaries;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QskIntervalF QskGraduationRenderer::boundaries() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
return m_data->boundaries;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
qreal QskGraduationRenderer::position() const
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
return m_data->position;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setPosition( qreal pos )
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
m_data->position = pos;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setRange( qreal from, qreal to )
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
setRange( QskIntervalF( from, to ) );
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setRange( const QskIntervalF& range )
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
m_data->range = range;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QskIntervalF QskGraduationRenderer::range() const
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
return m_data->range;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setTickmarks( const QskTickmarks& tickmarks )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
m_data->tickmarks = tickmarks;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
const QskTickmarks& QskGraduationRenderer::tickmarks() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
return m_data->tickmarks;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setSpacing( qreal spacing )
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
m_data->spacing = qMax( spacing, 0.0 );
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
qreal QskGraduationRenderer::spacing() const
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
|
|
|
return m_data->spacing;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setTickColor( const QColor& color )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
m_data->tickColor = color;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QColor QskGraduationRenderer::tickColor() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
return m_data->tickColor;
|
|
|
|
}
|
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
void QskGraduationRenderer::setTickMetrics( const QskGraduationMetrics& metrics )
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
2023-11-28 14:35:28 +01:00
|
|
|
m_data->metrics = metrics;
|
2023-11-28 13:36:47 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
const QskGraduationMetrics& QskGraduationRenderer::tickMetrics() const
|
2023-11-28 13:36:47 +01:00
|
|
|
{
|
2023-11-28 14:35:28 +01:00
|
|
|
return m_data->metrics;
|
2023-11-28 13:36:47 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setFont( const QFont& font )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
m_data->font = font;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QFont QskGraduationRenderer::font() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
return m_data->font;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setTextColors( const QskTextColors& textColors )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
m_data->textColors = textColors;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QskTextColors QskGraduationRenderer::textColors() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
return m_data->textColors;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
void QskGraduationRenderer::setColorFilter( const QskColorFilter& colorFilter )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
m_data->colorFilter = colorFilter;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
const QskColorFilter& QskGraduationRenderer::colorFilter() const
|
2023-04-24 11:55:36 +02:00
|
|
|
{
|
|
|
|
return m_data->colorFilter;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QSGNode* QskGraduationRenderer::updateNode(
|
2023-11-28 13:36:47 +01:00
|
|
|
const QskSkinnable* skinnable, QSGNode* node )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
enum Role : quint8 { Ticks = 1, Labels = 2 };
|
|
|
|
static const QVector< quint8 > roles = { Ticks, Labels };
|
|
|
|
|
|
|
|
const auto transform = qskScaleTransform(
|
|
|
|
m_data->edge, m_data->boundaries, m_data->range );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
|
|
|
if ( node == nullptr )
|
|
|
|
node = new QSGNode();
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
for ( auto role : roles )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
auto oldNode = QskSGNode::findChildNode( node, role );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
auto newNode = ( role == Ticks )
|
|
|
|
? updateTicksNode( transform, oldNode )
|
|
|
|
: updateLabelsNode( skinnable, transform, oldNode );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
QskSGNode::replaceChildNode( roles, role, node, oldNode, newNode );
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QSGNode* QskGraduationRenderer::updateTicksNode(
|
2023-11-28 13:36:47 +01:00
|
|
|
const QTransform& transform, QSGNode* node ) const
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
QskIntervalF backbone;
|
|
|
|
if ( m_data->flags & Backbone )
|
|
|
|
backbone = m_data->boundaries;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
const auto orientation = qskIsHorizontal( m_data->edge )
|
|
|
|
? Qt::Horizontal : Qt::Vertical;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
auto alignment = QskGraduationNode::Centered;
|
2020-12-05 15:09:31 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( !( m_data->flags & CenteredTickmarks ) )
|
|
|
|
{
|
|
|
|
switch( m_data->edge )
|
|
|
|
{
|
|
|
|
case Qt::LeftEdge:
|
|
|
|
case Qt::TopEdge:
|
2023-11-28 14:35:28 +01:00
|
|
|
alignment = QskGraduationNode::Leading;
|
2023-11-28 13:36:47 +01:00
|
|
|
break;
|
|
|
|
case Qt::BottomEdge:
|
|
|
|
case Qt::RightEdge:
|
2023-11-28 14:35:28 +01:00
|
|
|
alignment = QskGraduationNode::Trailing;
|
2023-11-28 13:36:47 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
auto graduationNode = QskSGNode::ensureNode< QskGraduationNode >( node );
|
2023-04-04 09:05:16 +02:00
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
graduationNode->setColor( m_data->tickColor );
|
|
|
|
graduationNode->setAxis( orientation, m_data->position, transform );
|
2023-11-28 15:19:49 +01:00
|
|
|
graduationNode->setTickMetrics( alignment, m_data->metrics );
|
2023-11-28 14:35:28 +01:00
|
|
|
graduationNode->setPixelAlignment( Qt::Horizontal | Qt::Vertical );
|
2020-12-05 15:09:31 +01:00
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
graduationNode->update( m_data->tickmarks, backbone );
|
2023-11-28 13:36:47 +01:00
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
return graduationNode;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QSGNode* QskGraduationRenderer::updateLabelsNode( const QskSkinnable* skinnable,
|
2023-11-28 13:36:47 +01:00
|
|
|
const QTransform& transform, QSGNode* node ) const
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2022-03-24 11:14:46 +01:00
|
|
|
const auto ticks = m_data->tickmarks.majorTicks();
|
2020-11-22 15:28:56 +01:00
|
|
|
if ( ticks.isEmpty() )
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if( node == nullptr )
|
|
|
|
node = new QSGNode;
|
|
|
|
|
2022-03-24 11:14:46 +01:00
|
|
|
const QFontMetricsF fm( m_data->font );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
|
|
|
auto nextNode = node->firstChild();
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
QRectF lastRect; // to skip overlapping label
|
2021-04-19 09:28:19 +02:00
|
|
|
|
2020-11-22 15:28:56 +01:00
|
|
|
for ( auto tick : ticks )
|
|
|
|
{
|
|
|
|
const auto label = labelAt( tick );
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
const auto role = qskLabelNodeRole( label );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( nextNode && QskSGNode::nodeRole( nextNode ) != role )
|
|
|
|
nextNode = qskRemoveTraillingNodes( node, nextNode );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
QSizeF size;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( label.canConvert< QString >() )
|
|
|
|
{
|
|
|
|
size = qskTextRenderSize( fm, label.toString() );
|
|
|
|
}
|
|
|
|
else if ( label.canConvert< QskGraphic >() )
|
|
|
|
{
|
|
|
|
const auto graphic = label.value< QskGraphic >();
|
|
|
|
if ( !graphic.isNull() )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
size.rheight() = fm.height();
|
|
|
|
size.rwidth() = graphic.widthForHeight( size.height() );
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
2023-11-28 13:36:47 +01:00
|
|
|
}
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( size.isEmpty() )
|
|
|
|
continue;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
const auto rect = labelRect( transform, tick, size );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( !lastRect.isEmpty() && lastRect.intersects( rect ) )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Label do overlap: in case it is the last tick we remove
|
|
|
|
the precessor - otherwise we simply skip this one
|
|
|
|
*/
|
2021-04-19 09:28:19 +02:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( tick != ticks.last() )
|
|
|
|
continue; // skip this label
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( auto obsoleteNode = nextNode
|
|
|
|
? nextNode->previousSibling() : node->lastChild() )
|
2022-05-30 15:16:46 +02:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
node->removeChildNode( obsoleteNode );
|
|
|
|
if ( obsoleteNode->flags() & QSGNode::OwnedByParent )
|
|
|
|
delete obsoleteNode;
|
2022-05-30 15:16:46 +02:00
|
|
|
}
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
nextNode = updateTickLabelNode( skinnable, nextNode, label, rect );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( nextNode)
|
|
|
|
{
|
|
|
|
lastRect = rect;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( nextNode->parent() != node )
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
QskSGNode::setNodeRole( nextNode, role );
|
|
|
|
node->appendChildNode( nextNode );
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
nextNode = nextNode->nextSibling();
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qskRemoveTraillingNodes( node, nextNode );
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QVariant QskGraduationRenderer::labelAt( qreal pos ) const
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
|
|
|
return QString::number( pos, 'g' );
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
// should be cached
|
2023-11-28 13:45:14 +01:00
|
|
|
QSizeF QskGraduationRenderer::boundingLabelSize() const
|
2020-11-22 15:28:56 +01:00
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
QSizeF boundingSize( 0.0, 0.0 );
|
|
|
|
|
2022-03-24 11:14:46 +01:00
|
|
|
const auto ticks = m_data->tickmarks.majorTicks();
|
2020-11-22 15:28:56 +01:00
|
|
|
if ( ticks.isEmpty() )
|
2023-11-28 13:36:47 +01:00
|
|
|
return boundingSize;
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2022-03-24 11:14:46 +01:00
|
|
|
const QFontMetricsF fm( m_data->font );
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2021-02-01 10:22:54 +01:00
|
|
|
const qreal h = fm.height();
|
2020-11-22 15:28:56 +01:00
|
|
|
|
|
|
|
for ( auto tick : ticks )
|
|
|
|
{
|
|
|
|
const auto label = labelAt( tick );
|
|
|
|
if ( label.isNull() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ( label.canConvert< QString >() )
|
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
boundingSize = boundingSize.expandedTo(
|
|
|
|
qskTextRenderSize( fm, label.toString() ) );
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
else if ( label.canConvert< QskGraphic >() )
|
|
|
|
{
|
|
|
|
const auto graphic = label.value< QskGraphic >();
|
|
|
|
if ( !graphic.isNull() )
|
|
|
|
{
|
2023-11-28 13:36:47 +01:00
|
|
|
const auto w = graphic.widthForHeight( h );
|
|
|
|
boundingSize.setWidth( qMax( boundingSize.width(), w ) );
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
}
|
2023-11-28 13:36:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return boundingSize;
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QRectF QskGraduationRenderer::labelRect(
|
2023-11-28 13:36:47 +01:00
|
|
|
const QTransform& transform, qreal tick, const QSizeF& labelSize ) const
|
|
|
|
{
|
|
|
|
const auto isHorizontal = qskIsHorizontal( m_data->edge );
|
|
|
|
|
2023-11-28 14:35:28 +01:00
|
|
|
const auto tickLength = m_data->metrics.maxLength();
|
|
|
|
|
|
|
|
auto offset = tickLength + m_data->spacing;
|
2023-11-28 13:36:47 +01:00
|
|
|
if ( m_data->flags & CenteredTickmarks )
|
2023-11-28 14:35:28 +01:00
|
|
|
offset -= 0.5 * tickLength;
|
2023-11-28 13:36:47 +01:00
|
|
|
|
|
|
|
const bool clampLabels = m_data->flags & ClampedLabels;
|
|
|
|
|
|
|
|
const qreal w = labelSize.width();
|
|
|
|
const qreal h = labelSize.height();
|
|
|
|
|
|
|
|
qreal x, y;
|
|
|
|
|
|
|
|
const ScaleMap map( isHorizontal, transform );
|
|
|
|
|
|
|
|
const auto tickPos = map.map( tick );
|
|
|
|
|
|
|
|
qreal min, max;
|
|
|
|
if ( clampLabels )
|
|
|
|
{
|
|
|
|
min = map.map( m_data->boundaries.lowerBound() );
|
|
|
|
max = map.map( m_data->boundaries.upperBound() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( isHorizontal )
|
|
|
|
{
|
|
|
|
x = tickPos - 0.5 * w;
|
|
|
|
|
|
|
|
if ( clampLabels )
|
|
|
|
x = qBound( min, x, max - w );
|
|
|
|
|
|
|
|
y = m_data->position + offset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
y = tickPos - 0.5 * h;
|
|
|
|
|
|
|
|
if ( clampLabels )
|
|
|
|
y = qBound( max, y, min - h );
|
|
|
|
|
|
|
|
x = m_data->position - offset - w;
|
|
|
|
}
|
2020-11-22 15:28:56 +01:00
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
return QRectF( x, y, w, h );
|
|
|
|
}
|
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
QSGNode* QskGraduationRenderer::updateTickLabelNode( const QskSkinnable* skinnable,
|
2023-11-28 13:36:47 +01:00
|
|
|
QSGNode* node, const QVariant& label, const QRectF& rect ) const
|
|
|
|
{
|
|
|
|
if ( label.canConvert< QString >() )
|
|
|
|
{
|
|
|
|
return QskSkinlet::updateTextNode( skinnable, node,
|
|
|
|
rect, Qt::AlignCenter, label.toString(), m_data->font,
|
|
|
|
QskTextOptions(), m_data->textColors, Qsk::Normal );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( label.canConvert< QskGraphic >() )
|
|
|
|
{
|
|
|
|
const auto alignment = qskIsHorizontal( m_data->edge )
|
|
|
|
? ( Qt::AlignHCenter | Qt::AlignBottom )
|
|
|
|
: ( Qt::AlignRight | Qt::AlignVCenter );
|
|
|
|
|
|
|
|
return QskSkinlet::updateGraphicNode(
|
|
|
|
skinnable, node, label.value< QskGraphic >(),
|
|
|
|
m_data->colorFilter, rect, alignment );
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 13:36:47 +01:00
|
|
|
return nullptr;
|
2020-11-22 15:28:56 +01:00
|
|
|
}
|
2023-11-28 13:36:47 +01:00
|
|
|
|
2023-11-28 13:45:14 +01:00
|
|
|
#include "moc_QskGraduationRenderer.cpp"
|