280 lines
7.9 KiB
C++
280 lines
7.9 KiB
C++
/******************************************************************************
|
|
* QSkinny - Copyright (C) The authors
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*****************************************************************************/
|
|
|
|
#include "QskPlotViewSkinlet.h"
|
|
#include "QskPlotView.h"
|
|
|
|
#include <QskGraduationRenderer.h>
|
|
#include <QskGraduationMetrics.h>
|
|
#include <QskTickmarks.h>
|
|
#include <QskTextColors.h>
|
|
#include <QskFunctions.h>
|
|
#include <QskSGNode.h>
|
|
#include <QskBoxBorderMetrics.h>
|
|
#include <QskFontRole.h>
|
|
|
|
static inline QskTextColors qskTextColors(
|
|
const QskSkinnable* skinnable, QskAspect aspect )
|
|
{
|
|
using A = QskAspect;
|
|
|
|
QskSkinHintStatus status;
|
|
|
|
auto textColor = skinnable->color( aspect | A::TextColor, &status );
|
|
|
|
if ( status.aspect.subControl() != aspect.subControl() )
|
|
{
|
|
// using the same color as the one for the ticks
|
|
textColor = skinnable->color( aspect );
|
|
}
|
|
|
|
return QskTextColors( textColor,
|
|
skinnable->color( aspect | A::StyleColor ),
|
|
skinnable->color( aspect | A::LinkColor ) );
|
|
}
|
|
|
|
static inline QskAspect qskAxisAspect( QskPlot::Axis axis )
|
|
{
|
|
using Q = QskPlotView;
|
|
using A = QskAspect;
|
|
|
|
return Q::AxisScale | ( ( axis == QskPlot::XBottom ) ? A::Bottom : A::Left );
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class Renderer : public QskGraduationRenderer
|
|
{
|
|
using Inherited = QskGraduationRenderer;
|
|
|
|
public:
|
|
Renderer( const QskPlotView* view, QskPlot::Axis axis )
|
|
: m_view( view )
|
|
, m_axis( axis )
|
|
{
|
|
using Q = QskPlotView;
|
|
using A = QskAspect;
|
|
|
|
setEdge( axis == QskPlot::XBottom ? Qt::BottomEdge : Qt::LeftEdge );
|
|
|
|
setBoundaries( view->boundaries( axis ) );
|
|
setTickmarks( view->tickmarks( axis ) );
|
|
|
|
const auto aspect = Q::AxisScale
|
|
| ( ( axis == QskPlot::XBottom ) ? A::Bottom : A::Left );
|
|
|
|
const auto flags = view->flagHint< QskGraduationRenderer::Flags >(
|
|
aspect | QskAspect::Style, QskGraduationRenderer::Backbone );
|
|
|
|
setFlags( flags );
|
|
|
|
setTickColor( view->color( aspect ) );
|
|
|
|
const auto graduation = view->effectiveSkinHint(
|
|
aspect | QskAspect::Metric | QskAspect::Graduation );
|
|
|
|
setTickMetrics( graduation.value< QskGraduationMetrics >() );
|
|
|
|
setSpacing( view->spacingHint( aspect ) );
|
|
setFont( view->effectiveFont( aspect ) );
|
|
|
|
setTextColors( qskTextColors( view, aspect ) );
|
|
}
|
|
|
|
qreal labelOffset() const
|
|
{
|
|
const auto length = tickMetrics().maxLength();
|
|
|
|
qreal off = length + spacing();
|
|
if ( flags() & CenteredTickmarks )
|
|
off -= 0.5 * length;
|
|
|
|
return off;
|
|
}
|
|
|
|
QVariant labelAt( qreal pos ) const override
|
|
{
|
|
return m_view->labelAt( m_axis, pos );
|
|
}
|
|
|
|
private:
|
|
const QskPlotView* m_view;
|
|
const QskPlot::Axis m_axis;
|
|
};
|
|
}
|
|
|
|
QskPlotViewSkinlet::QskPlotViewSkinlet( QskSkin* skin )
|
|
: Inherited( skin )
|
|
{
|
|
setNodeRoles( { PanelRole, CanvasRole, AxisRole } );
|
|
}
|
|
|
|
QskPlotViewSkinlet::~QskPlotViewSkinlet() = default;
|
|
|
|
QRectF QskPlotViewSkinlet::subControlRect( const QskSkinnable* skinnable,
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
|
{
|
|
using Q = QskPlotView;
|
|
|
|
if ( subControl == Q::Panel )
|
|
return contentsRect;
|
|
|
|
if ( subControl == Q::Canvas )
|
|
{
|
|
const auto view = static_cast< const QskPlotView* >( skinnable );
|
|
|
|
const auto rectX = sampleRect(
|
|
skinnable, contentsRect, Q::AxisScale, QskPlot::XBottom );
|
|
|
|
const auto rectY = sampleRect(
|
|
skinnable, contentsRect, Q::AxisScale, QskPlot::YLeft );
|
|
|
|
auto rect = view->subControlContentsRect( Q::Panel );
|
|
|
|
rect.setBottom( rectX.top() );
|
|
rect.setLeft( rectY.right() );
|
|
|
|
return rect;
|
|
}
|
|
|
|
return Inherited::subControlRect( skinnable, contentsRect, subControl );
|
|
}
|
|
|
|
QSGNode* QskPlotViewSkinlet::updateSubNode(
|
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
|
{
|
|
using Q = QskPlotView;
|
|
|
|
switch ( nodeRole )
|
|
{
|
|
case PanelRole:
|
|
return updateBoxNode( skinnable, node, Q::Panel );
|
|
|
|
case CanvasRole:
|
|
return updateBoxNode( skinnable, node, Q::Canvas );
|
|
|
|
case AxisRole:
|
|
return updateSeriesNode( skinnable, Q::AxisScale, node );
|
|
}
|
|
|
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
|
}
|
|
|
|
int QskPlotViewSkinlet::sampleCount(
|
|
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
|
|
{
|
|
using Q = QskPlotView;
|
|
|
|
if ( subControl == Q::AxisScale )
|
|
return 2; // for the moment only 2 axes
|
|
|
|
return Inherited::sampleCount( skinnable, subControl );
|
|
}
|
|
|
|
QRectF QskPlotViewSkinlet::sampleRect( const QskSkinnable* skinnable,
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl, int index ) const
|
|
{
|
|
if ( subControl == QskPlotView::AxisScale )
|
|
{
|
|
const auto axis = static_cast< QskPlot::Axis >( index );
|
|
return axisRect( skinnable, axis );
|
|
}
|
|
|
|
return Inherited::sampleRect( skinnable, contentsRect, subControl, index );
|
|
}
|
|
|
|
QSGNode* QskPlotViewSkinlet::updateSampleNode( const QskSkinnable* skinnable,
|
|
QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
|
|
{
|
|
using Q = QskPlotView;
|
|
|
|
if ( subControl == Q::AxisScale )
|
|
{
|
|
const auto axis = static_cast< QskPlot::Axis >( index );
|
|
return updateAxisNode( skinnable, axis, node );
|
|
}
|
|
|
|
return Inherited::updateSampleNode( skinnable, subControl, index, node );
|
|
}
|
|
|
|
QSizeF QskPlotViewSkinlet::sizeHint( const QskSkinnable* skinnable,
|
|
Qt::SizeHint which, const QSizeF& constraint ) const
|
|
{
|
|
return Inherited::sizeHint( skinnable, which, constraint );
|
|
}
|
|
|
|
QRectF QskPlotViewSkinlet::axisRect(
|
|
const QskSkinnable* skinnable, QskPlot::Axis axis ) const
|
|
{
|
|
using Q = QskPlotView;
|
|
using A = QskAspect;
|
|
|
|
const auto view = static_cast< const QskPlotView* >( skinnable );
|
|
auto rect = view->subControlContentsRect( Q::Panel );
|
|
|
|
const QskMargins paddingLeft = view->paddingHint( Q::AxisScale | A::Left );
|
|
const QskMargins paddingBottom = view->paddingHint( Q::AxisScale | A::Bottom );
|
|
|
|
qreal x0, y0;
|
|
{
|
|
const Renderer renderer( view, QskPlot::XBottom );
|
|
const auto sz = renderer.boundingLabelSize();
|
|
|
|
y0 = rect.bottom() - sz.height()
|
|
- renderer.labelOffset() - paddingBottom.height();
|
|
}
|
|
{
|
|
const Renderer renderer( view, QskPlot::YLeft );
|
|
const auto sz = renderer.boundingLabelSize();
|
|
|
|
x0 = rect.left() + sz.width()
|
|
+ renderer.labelOffset() + paddingLeft.width();
|
|
}
|
|
|
|
const auto canvasBorder = view->boxBorderMetricsHint( Q::Canvas );
|
|
|
|
if ( axis == QskPlot::XBottom )
|
|
{
|
|
rect.setTop( y0 );
|
|
rect.setLeft( x0 + canvasBorder.left() );
|
|
rect.setRight( rect.right() - canvasBorder.right() );
|
|
}
|
|
else
|
|
{
|
|
rect.setRight( x0 );
|
|
rect.setTop( rect.top() + canvasBorder.top() );
|
|
rect.setBottom( y0 - canvasBorder.bottom() );
|
|
}
|
|
|
|
return rect;
|
|
}
|
|
|
|
QSGNode* QskPlotViewSkinlet::updateAxisNode(
|
|
const QskSkinnable* skinnable, QskPlot::Axis axis, QSGNode* node ) const
|
|
{
|
|
using Q = QskPlotView;
|
|
|
|
const auto view = static_cast< const QskPlotView* >( skinnable );
|
|
|
|
const auto axisRect = sampleRect( view, view->contentsRect(), Q::AxisScale, axis );
|
|
const auto padding = view->paddingHint( qskAxisAspect( axis ) );
|
|
|
|
Renderer renderer( view, axis );
|
|
if ( axis == QskPlot::XBottom )
|
|
{
|
|
renderer.setPosition( axisRect.top() + padding.top() );
|
|
renderer.setRange( axisRect.left(), axisRect.right() );
|
|
}
|
|
else
|
|
{
|
|
renderer.setPosition( axisRect.right() - padding.right() );
|
|
renderer.setRange( axisRect.top(), axisRect.bottom() );
|
|
}
|
|
|
|
return renderer.updateNode( view, node );
|
|
}
|
|
|
|
#include "moc_QskPlotViewSkinlet.cpp"
|