Hello community,

here is the log from the commit of package kquickcharts for openSUSE:Factory 
checked in at 2020-12-15 12:29:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kquickcharts (Old)
 and      /work/SRC/openSUSE:Factory/.kquickcharts.new.2328 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kquickcharts"

Tue Dec 15 12:29:20 2020 rev:13 rq:855456 version:5.77.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/kquickcharts/kquickcharts.changes        
2020-11-23 10:32:58.209524372 +0100
+++ /work/SRC/openSUSE:Factory/.kquickcharts.new.2328/kquickcharts.changes      
2020-12-15 12:31:36.788059708 +0100
@@ -1,0 +2,9 @@
+Sat Dec  5 18:56:54 UTC 2020 - Christophe Giboudeaux <[email protected]>
+
+- Update to 5.77.0
+  * New feature release
+  * For more details please see:
+  * https://kde.org/announcements/kde-frameworks-5.77.0
+- Too many changes to list here.
+
+-------------------------------------------------------------------

Old:
----
  kquickcharts-5.76.0.tar.xz
  kquickcharts-5.76.0.tar.xz.sig

New:
----
  kquickcharts-5.77.0.tar.xz
  kquickcharts-5.77.0.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kquickcharts.spec ++++++
--- /var/tmp/diff_new_pack.LImXFa/_old  2020-12-15 12:31:37.472060259 +0100
+++ /var/tmp/diff_new_pack.LImXFa/_new  2020-12-15 12:31:37.476060262 +0100
@@ -16,14 +16,14 @@
 #
 
 
-%define _tar_path 5.76
+%define _tar_path 5.77
 # Full KF5 version (e.g. 5.33.0)
 %{!?_kf5_version: %global _kf5_version %{version}}
 # Last major and minor KF5 version (e.g. 5.33)
 %{!?_kf5_bugfix_version: %define _kf5_bugfix_version %(echo %{_kf5_version} | 
awk -F. '{print $1"."$2}')}
 %bcond_without lang
 Name:           kquickcharts
-Version:        5.76.0
+Version:        5.77.0
 Release:        0
 Summary:        Set of charts for QtQuick applications
 License:        LGPL-2.1-or-later
@@ -36,9 +36,9 @@
 %endif
 BuildRequires:  extra-cmake-modules >= %{_kf5_bugfix_version}
 BuildRequires:  kf5-filesystem
-BuildRequires:  cmake(Qt5Qml) >= 5.12.0
-BuildRequires:  cmake(Qt5Quick) >= 5.12.0
-BuildRequires:  cmake(Qt5QuickControls2) >= 5.12.0
+BuildRequires:  cmake(Qt5Qml) >= 5.13.0
+BuildRequires:  cmake(Qt5Quick) >= 5.13.0
+BuildRequires:  cmake(Qt5QuickControls2) >= 5.13.0
 Requires:       kirigami2
 Requires:       libqt5-qtquickcontrols2
 


++++++ kquickcharts-5.76.0.tar.xz -> kquickcharts-5.77.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kquickcharts-5.76.0/CMakeLists.txt 
new/kquickcharts-5.77.0/CMakeLists.txt
--- old/kquickcharts-5.76.0/CMakeLists.txt      2020-11-07 12:42:59.000000000 
+0100
+++ new/kquickcharts-5.77.0/CMakeLists.txt      2020-12-05 11:13:26.000000000 
+0100
@@ -1,12 +1,12 @@
 cmake_minimum_required(VERSION 3.5)
 
-set(KF5_VERSION "5.76.0") # handled by release scripts
-set(KF5_DEP_VERSION "5.76.0") # handled by release scripts
+set(KF5_VERSION "5.77.0") # handled by release scripts
+set(KF5_DEP_VERSION "5.77.0") # handled by release scripts
 
 project(KQuickCharts VERSION ${KF5_VERSION})
 
 include(FeatureSummary)
-find_package(ECM 5.76.0 NO_MODULE)
+find_package(ECM 5.77.0 NO_MODULE)
 set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake 
Modules." URL "https://commits.kde.org/extra-cmake-modules";)
 feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND 
FATAL_ON_MISSING_REQUIRED_PACKAGES)
 
@@ -27,7 +27,7 @@
 
 option(BUILD_EXAMPLES "Build example applications" OFF)
 
-set(REQUIRED_QT_VERSION 5.12.0)
+set(REQUIRED_QT_VERSION 5.13.0)
 find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Qml Quick 
QuickControls2)
 
 add_subdirectory(controls)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kquickcharts-5.76.0/controls/LineChartControl.qml 
new/kquickcharts-5.77.0/controls/LineChartControl.qml
--- old/kquickcharts-5.76.0/controls/LineChartControl.qml       2020-11-07 
12:42:59.000000000 +0100
+++ new/kquickcharts-5.77.0/controls/LineChartControl.qml       2020-12-05 
11:13:26.000000000 +0100
@@ -38,6 +38,8 @@
     property alias xAxisSource: xAxisLabels.source
     property alias yAxisSource: yAxisLabels.source
 
+    property alias pointDelegate: lineChart.pointDelegate
+
     background: Rectangle { color: Theme.backgroundColor }
 
     contentItem: Item {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kquickcharts-5.76.0/examples/charts/LineChart.qml 
new/kquickcharts-5.77.0/examples/charts/LineChart.qml
--- old/kquickcharts-5.76.0/examples/charts/LineChart.qml       2020-11-07 
12:42:59.000000000 +0100
+++ new/kquickcharts-5.77.0/examples/charts/LineChart.qml       2020-12-05 
11:13:26.000000000 +0100
@@ -53,6 +53,25 @@
                 ]
 
                 names: ["Example 1", "Example 2", "Example 3"]
+
+                pointDelegate: Item {
+                    Rectangle {
+                        anchors.centerIn: parent
+                        width: lineChart.lineWidth + 
Kirigami.Units.smallSpacing;
+                        height: width
+                        radius: width / 2;
+                        color: parent.Charts.LineChart.color
+
+                        MouseArea {
+                            id: mouse
+                            anchors.fill: parent
+                            hoverEnabled: true
+                        }
+
+                        ToolTip.visible: mouse.containsMouse
+                        ToolTip.text: "%1: 
%2".arg(parent.Charts.LineChart.name).arg(parent.Charts.LineChart.value)
+                    }
+                }
             }
         }
 
@@ -67,6 +86,7 @@
             Label { text: "Fill Opacity" }
             SpinBox { from: 0; to: 100; value: lineChart.fillOpacity * 100; 
onValueModified: lineChart.fillOpacity = value / 100; }
             CheckBox { text: "Stacked"; checked: lineChart.stacked; onToggled: 
lineChart.stacked = checked }
+            CheckBox { text: "Smooth"; checked: lineChart.chart.smooth; 
onToggled: lineChart.chart.smooth = checked }
         }
 
         Frame {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kquickcharts-5.76.0/src/LineChart.cpp 
new/kquickcharts-5.77.0/src/LineChart.cpp
--- old/kquickcharts-5.76.0/src/LineChart.cpp   2020-11-07 12:42:59.000000000 
+0100
+++ new/kquickcharts-5.77.0/src/LineChart.cpp   2020-12-05 11:13:26.000000000 
+0100
@@ -17,7 +17,9 @@
 #include "scenegraph/LineChartNode.h"
 #include "scenegraph/LineGridNode.h"
 
-QVector<QVector2D> interpolate(const QVector<QVector2D> &points, qreal start, 
qreal end, qreal height);
+QVector<QPointF> solveControlPoints(const QVector<QPointF> input);
+QVector<QPair<QPointF, QPointF>> calculateControlPoints(const 
QVector<QVector2D> &points, qreal height);
+QVector<QVector2D> interpolate(const QVector<QVector2D> &points, qreal height);
 
 QColor colorWithAlpha(const QColor &color, qreal opacity)
 {
@@ -29,6 +31,76 @@
     return result;
 }
 
+LineChartAttached::LineChartAttached(QObject* parent)
+    : QObject(parent)
+{
+}
+
+QVariant LineChartAttached::value() const
+{
+    return m_value;
+}
+
+void LineChartAttached::setValue(const QVariant& value)
+{
+    if (value == m_value) {
+        return;
+    }
+
+    m_value = value;
+    Q_EMIT valueChanged();
+}
+
+QColor LineChartAttached::color() const
+{
+    return m_color;
+}
+
+void LineChartAttached::setColor(const QColor& color)
+{
+    if (color == m_color) {
+        return;
+    }
+
+    m_color = color;
+    Q_EMIT colorChanged();
+}
+
+QString LineChartAttached::name() const
+{
+    return m_name;
+}
+
+void LineChartAttached::setName(const QString & newName)
+{
+    if (newName == m_name) {
+        return;
+    }
+
+    m_name = newName;
+    Q_EMIT nameChanged();
+}
+
+QString LineChartAttached::shortName() const
+{
+    if (m_shortName.isEmpty()) {
+        return m_name;
+    } else {
+        return m_shortName;
+    }
+}
+
+void LineChartAttached::setShortName(const QString & newShortName)
+{
+    if (newShortName == m_shortName) {
+        return;
+    }
+
+    m_shortName = newShortName;
+    Q_EMIT shortNameChanged();
+}
+
+
 LineChart::LineChart(QQuickItem *parent)
     : XYChart(parent)
 {
@@ -56,7 +128,7 @@
     }
 
     m_smooth = smooth;
-    update();
+    polish();
     Q_EMIT smoothChanged();
 }
 
@@ -98,21 +170,109 @@
     Q_EMIT fillColorSourceChanged();
 }
 
-QSGNode *LineChart::updatePaintNode(QSGNode *node, 
QQuickItem::UpdatePaintNodeData *data)
+QQmlComponent *LineChart::pointDelegate() const
 {
-    Q_UNUSED(data);
+    return m_pointDelegate;
+}
 
-    if (!node) {
-        node = new QSGNode();
+void LineChart::setPointDelegate(QQmlComponent *newPointDelegate)
+{
+    if (newPointDelegate == m_pointDelegate) {
+        return;
     }
 
+    m_pointDelegate = newPointDelegate;
+    for (auto entry : qAsConst(m_pointDelegates)) {
+        qDeleteAll(entry);
+    }
+    m_pointDelegates.clear();
+    polish();
+    Q_EMIT pointDelegateChanged();
+}
+
+void LineChart::updatePolish()
+{
     if (m_rangeInvalid) {
         updateComputedRange();
         m_rangeInvalid = false;
     }
 
-    if (stacked()) {
-        m_previousValues.clear();
+    QVector<QVector2D> previousValues;
+
+    const auto range = computedRange();
+    const auto sources = valueSources();
+    for (int i = 0; i < sources.size(); ++i) {
+        auto valueSource = sources.at(i);
+
+        float stepSize = width() / (range.distanceX - 1);
+        QVector<QVector2D> values(range.distanceX);
+        auto generator = [&, i = range.startX]() mutable -> QVector2D {
+            float value = 0;
+            if (range.distanceY != 0) {
+                value = (valueSource->item(i).toFloat() - range.startY) / 
range.distanceY;
+            }
+
+            auto result = QVector2D{direction() == Direction::ZeroAtStart ? i 
* stepSize
+                                    : float(boundingRect().right()) - i * 
stepSize, value};
+            i++;
+            return result;
+        };
+
+        if (direction() == Direction::ZeroAtStart) {
+            std::generate_n(values.begin(), range.distanceX, generator);
+        } else {
+            std::generate_n(values.rbegin(), range.distanceX, generator);
+        }
+
+        if (stacked() && !previousValues.isEmpty()) {
+            if (values.size() != previousValues.size()) {
+                qWarning() << "Value source" << valueSource->objectName()
+                        << "has a different number of elements from the 
previuous source. Ignoring stacking for this source.";
+            } else {
+                std::for_each(values.begin(), values.end(), [previousValues, i 
= 0](QVector2D &point) mutable {
+                    point.setY(point.y() + previousValues.at(i++).y());
+                });
+            }
+        }
+        previousValues = values;
+
+        if (m_pointDelegate) {
+            auto& delegates = m_pointDelegates[valueSource];
+            if (delegates.size() != values.size()) {
+                qDeleteAll(delegates);
+                createPointDelegates(values, i);
+            } else {
+                for (int item = 0; item < values.size(); ++item) {
+                    auto delegate = delegates.at(item);
+                    updatePointDelegate(delegate, values.at(item), 
valueSource->item(item), i);
+                }
+            }
+        }
+
+        if (m_smooth) {
+            m_values[valueSource] = interpolate(values, height());
+        } else {
+            m_values[valueSource] = values;
+        }
+    }
+
+    const auto pointKeys = m_pointDelegates.keys();
+    for (auto key : pointKeys) {
+        if (!sources.contains(key)) {
+            qDeleteAll(m_pointDelegates[key]);
+            m_pointDelegates.remove(key);
+        }
+    }
+
+    update();
+}
+
+QSGNode *LineChart::updatePaintNode(QSGNode *node, 
QQuickItem::UpdatePaintNodeData *data)
+{
+    Q_UNUSED(data);
+
+    if (!node) {
+        node = new QSGNode();
     }
 
     const auto sources = valueSources();
@@ -141,7 +301,16 @@
 void LineChart::onDataChanged()
 {
     m_rangeInvalid = true;
-    update();
+    polish();
+}
+
+void LineChart::geometryChanged(const QRectF& newGeometry, const QRectF& 
oldGeometry)
+{
+    XYChart::geometryChanged(newGeometry, oldGeometry);
+
+    if (newGeometry != oldGeometry) {
+        polish();
+    }
 }
 
 void LineChart::updateLineNode(LineChartNode *node, const QColor &lineColor, 
const QColor &fillColor, ChartDataSource *valueSource)
@@ -155,112 +324,159 @@
     node->setFillColor(fillColor);
     node->setLineWidth(m_lineWidth);
 
-    auto range = computedRange();
+    auto values = m_values.value(valueSource);
+    node->setValues(values);
+}
 
-    float stepSize = width() / (range.distanceX - 1);
-    QVector<QVector2D> values(range.distanceX);
-    auto generator = [&, i = range.startX]() mutable -> QVector2D {
-        float value = 0;
-        if (range.distanceY != 0) {
-            value = (valueSource->item(i).toFloat() - range.startY) / 
range.distanceY;
-        }
+QVector<QVector2D> interpolate(const QVector<QVector2D> &points, qreal height) 
//, qreal start, qreal end, qreal height)
+{
+    if (points.size() < 2) {
+        return points;
+    }
 
-        auto result = QVector2D{direction() == Direction::ZeroAtStart ? i * 
stepSize : float(boundingRect().right()) - i * stepSize,
-                                value};
-        i++;
-        return result;
-    };
+    auto controlPoints = calculateControlPoints(points, height);
 
-    if (direction() == Direction::ZeroAtStart) {
-        std::generate_n(values.begin(), range.distanceX, generator);
-    } else {
-        std::generate_n(values.rbegin(), range.distanceX, generator);
+    QPainterPath path;
+    path.moveTo(0.0, points.first().y() * height);
+
+    for (int i = 0; i < points.size() - 1; ++i) {
+        auto controlPoint = controlPoints.at(i);
+        auto nextPoint = QPointF{points.at(i + 1).x(), points.at(i + 1).y() * 
height};
+        path.cubicTo(controlPoint.first, controlPoint.second, nextPoint);
     }
 
-    if (stacked() && !m_previousValues.isEmpty()) {
-        if (values.size() != m_previousValues.size()) {
-            qWarning() << "Value source" << valueSource->objectName()
-                       << "has a different number of elements from the 
previuous source. Ignoring stacking for this source.";
-        } else {
-            std::for_each(
-                values.begin(), values.end(), [this, i = 0](QVector2D &point) 
mutable { point.setY(point.y() + m_previousValues.at(i++).y()); });
+    QVector<QVector2D> result;
+
+    const auto polygons = path.toSubpathPolygons();
+    auto pointCount = std::accumulate(polygons.begin(), polygons.end(), 0, 
[](int current, const QPolygonF &polygon) {
+        return current + polygon.size();
+    });
+    result.reserve(pointCount);
+
+    for (const auto &polygon : polygons) {
+        for (auto point : polygon) {
+            result.append(QVector2D{float(point.x()), float(point.y() / 
height)});
         }
     }
-    m_previousValues = values;
 
-    if (m_smooth) {
-        values = interpolate(values, 0.0, width(), height());
+    return result;
+}
+
+void LineChart::createPointDelegates(const QVector<QVector2D> &values, int 
sourceIndex)
+{
+    auto valueSource = valueSources().at(sourceIndex);
+
+    QVector<QQuickItem*> delegates;
+    for (int i = 0; i < values.size(); ++i) {
+        auto delegate = 
qobject_cast<QQuickItem*>(m_pointDelegate->beginCreate(qmlContext(m_pointDelegate)));
+        if (!delegate) {
+            qWarning() << "Delegate creation for point" << i << "of value 
source" << valueSource->objectName()
+                       << "failed, make sure pointDelegate is a QQuickItem";
+            delegate = new QQuickItem(this);
+        }
+
+        delegate->setParent(this);
+        delegate->setParentItem(this);
+        updatePointDelegate(delegate, values.at(i), valueSource->item(i), 
sourceIndex);
+
+        m_pointDelegate->completeCreate();
+
+        delegates.append(delegate);
     }
 
-    node->setValues(values);
+    m_pointDelegates.insert(valueSource, delegates);
 }
 
-QVector<QVector2D> interpolate(const QVector<QVector2D> &points, qreal start, 
qreal end, qreal height)
+void LineChart::updatePointDelegate(QQuickItem *delegate, const QVector2D 
&position, const QVariant &value, int sourceIndex)
 {
-    QPainterPath path;
-    if (points.size() < 4)
-        return points;
+    auto pos = QPointF{position.x() - delegate->width() / 2, (1.0 - 
position.y()) * height() - delegate->height() / 2};
+    delegate->setPosition(pos);
 
-    const auto sixth = 1.f / 6.f;
+    auto attached = 
static_cast<LineChartAttached*>(qmlAttachedPropertiesObject<LineChart>(delegate,
 true));
+    attached->setValue(value);
+    attached->setColor(colorSource() ? 
colorSource()->item(sourceIndex).value<QColor>() : Qt::black);
+    attached->setName(nameSource() ? 
nameSource()->item(sourceIndex).toString() : QString{});
+    attached->setShortName(shortNameSource() ? 
shortNameSource()->item(sourceIndex).toString() : QString{});
+}
 
-    const qreal xDelta = (end - start) / (points.count() - 3);
-    qreal x = start - xDelta;
+QVector<QPair<QPointF, QPointF>> calculateControlPoints(const 
QVector<QVector2D> &points, qreal height)
+{
+    // This is based on
+    // 
https://www.codeproject.com/Articles/31859/Draw-a-Smooth-Curve-through-a-Set-of-2D-Points-wit
+    // and calculates the control points based on the derivative of the curve.
 
-    path.moveTo(start, points[0].y() * height);
+    auto count = points.size() - 1;
+    QVector<QPair<QPointF, QPointF>> result(count);
 
-    for (int i = 1; i < points.count() - 2; i++) {
-        // This code was:
-        //
-        // QMatrix4x4 matrix(   0,   1,   0,    0,
-        //                   -1/6,   1, 1/6,    0,
-        //                      0, 1/6,   1, -1/6,
-        //                      0,   0,   1,    0);
-        // QMatrix4x4 p(x + xDelta * 0, points[i - 1].y() * height, 0, 0,
-        //              x + xDelta * 1, points[i + 0].y() * height, 0, 0,
-        //              x + xDelta * 2, points[i + 1].y() * height, 0, 0,
-        //              x + xDelta * 3, points[i + 2].y() * height, 0, 0)
-        // QMatrix4x4 res = matrix * p;
-        // path.cubicTo(res(1,0), res(1, 1), res(2, 0), res(2, 1), res(3, 0), 
res(3, 1))
-        //
-        // The below calculations calculate the used elements from the matrix 
directly, avoiding
-        // most of an expensive matrix multiplication.
-
-        auto p0 = points[i - 1].y() * height;
-        auto p1 = points[i].y() * height;
-        auto p2 = points[i + 1].y() * height;
-        auto p3 = points[i + 2].y() * height;
-
-        //res(1, 0) = (-1/6, 1, 1/6, 0) dot (x, x + xDelta, x + xDelta * 2, x 
+ xDelta * 3)
-        auto res10 = (3 * x + 4 * xDelta) / 3.f;
-        //res(1, 1) = (-1/6, 1, 1/6, 0) dot (p[i-1].y, p[i].y, p[i+1].y, 
p[i+2].y)
-        auto res11 = -sixth * p0 + p1 + sixth * p2;
-        //res(2, 0) = (0, 1/6, 1, -1/6) dot (x, x + xDelta, x + xDelta * 2, x 
+ xDelta * 3)
-        auto res20 = (3 * x + 5 * xDelta) / 3.f;
-        //res(2, 1) = (0, 1/6, 1, -1/6) dot (p[i-1].y, p[i].y, p[i+1].y, 
p[i+2].y)
-        auto res21 = sixth * p1 + p2 + -sixth * p3;
-        //res(3, 0) = (0, 0, 1, 0) dot (x, x + xDelta, x + xDelta * 2, x + 
xDelta * 3)
-        auto res30 = x + 2 * xDelta;
-        //res(3, 1) = (0, 0, 1, 0) dot (p[i-1].y, p[i].y, p[i+1].y, p[i+2].y)
-        auto res31 = p2;
+    const auto first = QPointF{points.first().x(), points.first().y() * 
height};
+    const auto last = QPointF{points.last().x(), points.last().y() * height};
 
-        path.cubicTo(res10, res11, res20, res21, res30, res31);
+    if (count == 1) {
+        auto &controlPoint = result[0];
 
-        x += xDelta;
-    }
+        controlPoint.first.rx() = (2.0 * first.x() + last.x()) / 3.0;
+        controlPoint.first.ry() = (2.0 * first.y() + last.y()) / 3.0;
 
-    QVector<QVector2D> result;
+        controlPoint.second.rx() = 2.0 * controlPoint.first.x() - first.x();
+        controlPoint.second.ry() = 2.0 * controlPoint.first.y() - first.y();
 
-    const auto polygons = path.toSubpathPolygons();
-    auto pointCount = std::accumulate(polygons.begin(), polygons.end(), 0, 
[](int current, const QPolygonF &polygon) {
-        return current + polygon.size();
+        return result;
+    }
+
+    QVector<QPointF> coordinates(count);
+    std::generate_n(coordinates.begin() + 1, count - 2, [&points, height, i = 
1]() mutable {
+        auto x = 4.0 * points[i].x() + 2.0 * points[i + 1].x();
+        auto y = 4.0 * points[i].y() * height + 2.0 * points[i + 1].y() * 
height;
+        i++;
+        return QPointF{x, y};
     });
-    result.reserve(pointCount);
 
-    for (const auto &polygon : polygons) {
-        for (auto point : polygon) {
-            result.append(QVector2D{float(point.x()), float(point.y() / 
height)});
+    coordinates.first().rx() = first.x() + 2.0 * points.at(1).x();
+    coordinates.first().ry() = first.y() + 2.0 * points.at(1).y() * height;
+    coordinates.last().rx() = (8.0 * points.at(count - 1).x() + last.x()) / 
2.0;
+    coordinates.last().ry() = (8.0 * points.at(count - 1).y() * height + 
last.y()) / 2.0;
+
+    const auto solved = solveControlPoints(coordinates);
+
+    for (int i = 0; i < count; ++i) {
+        auto &controlPoint = result[i];
+        controlPoint.first = solved[i];
+
+        if (i < count - 1) {
+            controlPoint.second = QPointF{
+                2.0 * points.at(i + 1).x() - solved.at(i + 1).x(),
+                2.0 * points.at(i + 1).y() * height - solved.at(i + 1).y()
+            };
+        } else {
+            controlPoint.second = QPointF{
+                (last.x() + solved.at(count - 1).x()) / 2.0,
+                (last.y() + solved.at(count - 1).y()) / 2.0
+            };
         }
     }
 
     return result;
+}
+
+QVector<QPointF> solveControlPoints(const QVector<QPointF> input)
+{
+    auto count = input.size();
+    QVector<QPointF> result(count);
+    QVector<double> temp(count);
+
+    double b = 2.0;
+    result.first() = input.first() / b;
+
+    for (int i = 1; i < count; ++i) {
+        temp[i] = 1.0 / b;
+        b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
+        result[i].rx() = (input[i].x() - result[i - 1].x()) / b;
+        result[i].ry() = (input[i].y() - result[i - 1].y()) / b;
+    }
+
+    for (int i = 1; i < count; ++i) {
+        result[count - i - 1] -= temp[count - i] * result[count - i];
+    }
+
+    return result;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kquickcharts-5.76.0/src/LineChart.h 
new/kquickcharts-5.77.0/src/LineChart.h
--- old/kquickcharts-5.76.0/src/LineChart.h     2020-11-07 12:42:59.000000000 
+0100
+++ new/kquickcharts-5.77.0/src/LineChart.h     2020-12-05 11:13:26.000000000 
+0100
@@ -15,13 +15,64 @@
 class LineChartNode;
 
 /**
+ * An attached property that is exposed to point delegates created in line 
charts.
+ *
+ * \sa LineChart::pointDelegate
+ */
+class LineChartAttached : public QObject
+{
+    Q_OBJECT
+    /**
+     * The value at the current point.
+     */
+    Q_PROPERTY(QVariant value READ value NOTIFY valueChanged)
+    /**
+     * The color at the current point.
+     */
+    Q_PROPERTY(QColor color READ color NOTIFY colorChanged)
+    /**
+     * The name at the current point.
+     */
+    Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+    /**
+     * The short name at the current point.
+     */
+    Q_PROPERTY(QString shortName READ shortName NOTIFY shortNameChanged)
+
+public:
+    LineChartAttached(QObject *parent = nullptr);
+
+    QVariant value() const;
+    void setValue(const QVariant &value);
+    Q_SIGNAL void valueChanged();
+
+    QColor color() const;
+    void setColor(const QColor &color);
+    Q_SIGNAL void colorChanged();
+
+    QString name() const;
+    void setName(const QString &newName);
+    Q_SIGNAL void nameChanged();
+
+    QString shortName() const;
+    void setShortName(const QString & newShortName);
+    Q_SIGNAL void shortNameChanged();
+
+private:
+    QVariant m_value;
+    QColor m_color;
+    QString m_name;
+    QString m_shortName;
+};
+
+/**
  * A line chart.
  *
  * ## Usage example
  *
  * \snippet snippets/linechart.qml example
  *
- * \image html linechart.png "The resulting bar chart."
+ * \image html linechart.png "The resulting line chart."
  */
 class LineChart : public XYChart
 {
@@ -49,6 +100,19 @@
      * with the fillOpacity used as its opacity.
      */
     Q_PROPERTY(ChartDataSource *fillColorSource READ fillColorSource WRITE 
setFillColorSource NOTIFY fillColorSourceChanged)
+    /**
+     * A delegate that will be placed at each line chart point.
+     *
+     * When this is not null, the specified component will be used to
+     * instantiate an object for each point in the chart. These objects will
+     * then be placed centered at positions corresponding to the points on the
+     * chart. Each instance will have access to the attached properties of
+     * LineChartAttached through LineChart attached object.
+     *
+     * \note The component assigned to this property is expected to create a
+     *       QQuickItem, since the created object needs to be positioned.
+     */
+    Q_PROPERTY(QQmlComponent *pointDelegate READ pointDelegate WRITE 
setPointDelegate NOTIFY pointDelegateChanged)
 
 public:
     explicit LineChart(QQuickItem *parent = nullptr);
@@ -69,19 +133,36 @@
     void setFillColorSource(ChartDataSource *newFillColorSource);
     Q_SIGNAL void fillColorSourceChanged();
 
+    QQmlComponent *pointDelegate() const;
+    void setPointDelegate(QQmlComponent *newPointDelegate);
+    Q_SIGNAL void pointDelegateChanged();
+
+    static LineChartAttached *qmlAttachedProperties(QObject *object)
+    {
+        return new LineChartAttached(object);
+    }
+
 protected:
+    void updatePolish() override;
     QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData 
*data) override;
     void onDataChanged() override;
+    void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) 
override;
 
 private:
     void updateLineNode(LineChartNode *node, const QColor &lineColor, const 
QColor &fillColor, ChartDataSource *valueSource);
+    void createPointDelegates(const QVector<QVector2D> &values, int 
sourceIndex);
+    void updatePointDelegate(QQuickItem *delegate, const QVector2D &position, 
const QVariant &value, int sourceIndex);
 
     bool m_smooth = false;
     qreal m_lineWidth = 1.0;
     qreal m_fillOpacity = 0.0;
     bool m_rangeInvalid = true;
-    QVector<QVector2D> m_previousValues;
     ChartDataSource *m_fillColorSource = nullptr;
+    QHash<ChartDataSource*, QVector<QVector2D>> m_values;
+    QQmlComponent *m_pointDelegate = nullptr;
+    QHash<ChartDataSource*, QVector<QQuickItem*>> m_pointDelegates;
 };
 
+QML_DECLARE_TYPEINFO(LineChart, QML_HAS_ATTACHED_PROPERTIES)
+
 #endif // LINECHART_H
_______________________________________________
openSUSE Commits mailing list -- [email protected]
To unsubscribe, email [email protected]
List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette
List Archives: 
https://lists.opensuse.org/archives/list/[email protected]

Reply via email to