diff options
Diffstat (limited to 'src/quickcontrols/universal/impl')
10 files changed, 915 insertions, 0 deletions
diff --git a/src/quickcontrols/universal/impl/CMakeLists.txt b/src/quickcontrols/universal/impl/CMakeLists.txt new file mode 100644 index 0000000000..078e42e217 --- /dev/null +++ b/src/quickcontrols/universal/impl/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## qtquickcontrols2universalstyleimplplugin Plugin: +##################################################################### + +set(qml_files + "CheckIndicator.qml" + "RadioIndicator.qml" + "SwitchIndicator.qml" +) + +qt_internal_add_qml_module(qtquickcontrols2universalstyleimplplugin + URI "QtQuick.Controls.Universal.impl" + VERSION "${PROJECT_VERSION}" + PAST_MAJOR_VERSIONS 2 + CLASS_NAME QtQuickControls2UniversalStyleImplPlugin + DEPENDENCIES + QtQuick/auto + PLUGIN_TARGET qtquickcontrols2universalstyleimplplugin + NO_PLUGIN_OPTIONAL + SOURCES + qquickuniversalbusyindicator.cpp qquickuniversalbusyindicator_p.h + qquickuniversalfocusrectangle.cpp qquickuniversalfocusrectangle_p.h + qquickuniversalprogressbar.cpp qquickuniversalprogressbar_p.h + QML_FILES + ${qml_files} + DEFINES + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::QmlPrivate + Qt::QuickControls2ImplPrivate + Qt::QuickPrivate + Qt::QuickTemplates2Private +) diff --git a/src/quickcontrols/universal/impl/CheckIndicator.qml b/src/quickcontrols/universal/impl/CheckIndicator.qml new file mode 100644 index 0000000000..b78e57570d --- /dev/null +++ b/src/quickcontrols/universal/impl/CheckIndicator.qml @@ -0,0 +1,48 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +import QtQuick +import QtQuick.Templates as T +import QtQuick.Controls.impl +import QtQuick.Controls.Universal + +Rectangle { + id: indicator + implicitWidth: 20 + implicitHeight: 20 + + color: !control.enabled ? "transparent" : + control.down && !partiallyChecked ? control.Universal.baseMediumColor : + control.checkState === Qt.Checked ? control.Universal.accent : "transparent" + border.color: !control.enabled ? control.Universal.baseLowColor : + control.down ? control.Universal.baseMediumColor : + control.checked ? control.Universal.accent : control.Universal.baseMediumHighColor + border.width: 2 // CheckBoxBorderThemeThickness + + property Item control + readonly property bool partiallyChecked: control.checkState === Qt.PartiallyChecked + + ColorImage { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + visible: indicator.control.checkState === Qt.Checked + color: !indicator.control.enabled ? indicator.control.Universal.baseLowColor : indicator.control.Universal.chromeWhiteColor + source: "qrc:/qt-project.org/imports/QtQuick/Controls/Universal/images/checkmark.png" + } + + Rectangle { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: indicator.partiallyChecked ? parent.width / 2 : parent.width + height: indicator.partiallyChecked ? parent.height / 2 : parent.height + + visible: !indicator.control.pressed && enabled && indicator.control.hovered || indicator.partiallyChecked + color: !indicator.partiallyChecked ? "transparent" : + !indicator.control.enabled ? indicator.control.Universal.baseLowColor : + indicator.control.down ? indicator.control.Universal.baseMediumColor : + indicator.control.hovered ? indicator.control.Universal.baseHighColor : indicator.control.Universal.baseMediumHighColor + border.width: indicator.partiallyChecked ? 0 : 2 // CheckBoxBorderThemeThickness + border.color: indicator.control.Universal.baseMediumLowColor + } +} diff --git a/src/quickcontrols/universal/impl/RadioIndicator.qml b/src/quickcontrols/universal/impl/RadioIndicator.qml new file mode 100644 index 0000000000..73fc2e964f --- /dev/null +++ b/src/quickcontrols/universal/impl/RadioIndicator.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls.Universal + +Rectangle { + id: indicator + implicitWidth: 20 + implicitHeight: 20 + radius: width / 2 + color: "transparent" + border.width: 2 // RadioButtonBorderThemeThickness + border.color: control.checked ? "transparent" : + !control.enabled ? control.Universal.baseLowColor : + control.down ? control.Universal.baseMediumColor : + control.hovered ? control.Universal.baseHighColor : control.Universal.baseMediumHighColor + + property var control + + Rectangle { + id: checkOuterEllipse + width: parent.width + height: parent.height + + radius: width / 2 + opacity: indicator.control.checked ? 1 : 0 + color: "transparent" + border.width: 2 // RadioButtonBorderThemeThickness + border.color: !indicator.control.enabled ? indicator.control.Universal.baseLowColor : + indicator.control.down ? indicator.control.Universal.baseMediumColor : indicator.control.Universal.accent + } + + Rectangle { + id: checkGlyph + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: parent.width / 2 + height: parent.height / 2 + + radius: width / 2 + opacity: indicator.control.checked ? 1 : 0 + color: !indicator.control.enabled ? indicator.control.Universal.baseLowColor : + indicator.control.down ? indicator.control.Universal.baseMediumColor : + indicator.control.hovered ? indicator.control.Universal.baseHighColor : indicator.control.Universal.baseMediumHighColor + } +} diff --git a/src/quickcontrols/universal/impl/SwitchIndicator.qml b/src/quickcontrols/universal/impl/SwitchIndicator.qml new file mode 100644 index 0000000000..f88094becc --- /dev/null +++ b/src/quickcontrols/universal/impl/SwitchIndicator.qml @@ -0,0 +1,48 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +import QtQuick +import QtQuick.Templates as T +import QtQuick.Controls.Universal + +Item { + id: indicator + implicitWidth: 44 + implicitHeight: 20 + + property T.AbstractButton control + + Rectangle { + width: parent.width + height: parent.height + + radius: 10 + color: !indicator.control.enabled ? "transparent" : + indicator.control.pressed ? indicator.control.Universal.baseMediumColor : + indicator.control.checked ? indicator.control.Universal.accent : "transparent" + border.color: !indicator.control.enabled ? indicator.control.Universal.baseLowColor : + indicator.control.checked && !indicator.control.pressed ? indicator.control.Universal.accent : + indicator.control.hovered && !indicator.control.checked && !indicator.control.pressed ? indicator.control.Universal.baseHighColor : indicator.control.Universal.baseMediumColor + opacity: enabled && indicator.control.hovered && indicator.control.checked && !indicator.control.pressed ? (indicator.control.Universal.theme === Universal.Light ? 0.7 : 0.9) : 1.0 + border.width: 2 + } + + Rectangle { + width: 10 + height: 10 + radius: 5 + + color: !indicator.control.enabled ? indicator.control.Universal.baseLowColor : + indicator.control.pressed || indicator.control.checked ? indicator.control.Universal.chromeWhiteColor : + indicator.control.hovered && !indicator.control.checked ? indicator.control.Universal.baseHighColor : indicator.control.Universal.baseMediumHighColor + + x: Math.max(5, Math.min(parent.width - width - 5, + indicator.control.visualPosition * parent.width - (width / 2))) + y: (parent.height - height) / 2 + + Behavior on x { + enabled: !indicator.control.pressed + SmoothedAnimation { velocity: 200 } + } + } +} diff --git a/src/quickcontrols/universal/impl/qquickuniversalbusyindicator.cpp b/src/quickcontrols/universal/impl/qquickuniversalbusyindicator.cpp new file mode 100644 index 0000000000..d0fdd925cd --- /dev/null +++ b/src/quickcontrols/universal/impl/qquickuniversalbusyindicator.cpp @@ -0,0 +1,220 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickuniversalbusyindicator_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qeasingcurve.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qsgadaptationlayer_p.h> +#include <QtQuickControls2Impl/private/qquickanimatednode_p.h> + +QT_BEGIN_NAMESPACE + +static const int PhaseCount = 6; +static const int Interval = 167; +static const int TotalDuration = 4052; + +class QQuickUniversalBusyIndicatorNode : public QQuickAnimatedNode +{ +public: + QQuickUniversalBusyIndicatorNode(QQuickUniversalBusyIndicator *item); + + void updateCurrentTime(int time) override; + void sync(QQuickItem *item) override; + +private: + struct Phase { + Phase() = default; + Phase(int d, qreal f, qreal t, QEasingCurve::Type c) : duration(d), from(f), to(t), curve(c) { } + int duration = 0; + qreal from = 0; + qreal to = 0; + QEasingCurve curve = QEasingCurve::Linear; + }; + + Phase m_phases[PhaseCount]; +}; + +QQuickUniversalBusyIndicatorNode::QQuickUniversalBusyIndicatorNode(QQuickUniversalBusyIndicator *item) + : QQuickAnimatedNode(item) +{ + setLoopCount(Infinite); + setDuration(TotalDuration); + setCurrentTime(item->elapsed()); + + m_phases[0] = Phase(433, -110, 10, QEasingCurve::BezierSpline); + m_phases[1] = Phase(767, 10, 93, QEasingCurve::Linear ); + m_phases[2] = Phase(417, 93, 205, QEasingCurve::BezierSpline); + m_phases[3] = Phase(400, 205, 357, QEasingCurve::BezierSpline); + m_phases[4] = Phase(766, 357, 439, QEasingCurve::Linear ); + m_phases[5] = Phase(434, 439, 585, QEasingCurve::BezierSpline); + + m_phases[0].curve.addCubicBezierSegment(QPointF(0.02, 0.33), QPointF(0.38, 0.77), QPointF(1.00, 1.00)); + m_phases[2].curve.addCubicBezierSegment(QPointF(0.57, 0.17), QPointF(0.95, 0.75), QPointF(1.00, 1.00)); + m_phases[3].curve.addCubicBezierSegment(QPointF(0.00, 0.19), QPointF(0.07, 0.72), QPointF(1.00, 1.00)); + m_phases[5].curve.addCubicBezierSegment(QPointF(0.00, 0.00), QPointF(0.95, 0.37), QPointF(1.00, 1.00)); +} + +void QQuickUniversalBusyIndicatorNode::updateCurrentTime(int time) +{ + int nodeIndex = 0; + int count = childCount(); + QSGTransformNode *transformNode = static_cast<QSGTransformNode *>(firstChild()); + while (transformNode) { + Q_ASSERT(transformNode->type() == QSGNode::TransformNodeType); + + QSGOpacityNode *opacityNode = static_cast<QSGOpacityNode *>(transformNode->firstChild()); + Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType); + + int begin = nodeIndex * Interval; + int end = TotalDuration - (PhaseCount - nodeIndex - 1) * Interval; + + bool visible = time >= begin && time <= end; + opacityNode->setOpacity(visible ? 1.0 : 0.0); + + if (visible) { + int phaseIndex, remain = time, elapsed = 0; + for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) { + if (remain <= m_phases[phaseIndex].duration + begin) + break; + remain -= m_phases[phaseIndex].duration; + elapsed += m_phases[phaseIndex].duration; + } + + const Phase &phase = m_phases[phaseIndex]; + + qreal from = phase.from - nodeIndex * count; + qreal to = phase.to - nodeIndex * count; + qreal pos = time - elapsed - begin; + + qreal value = phase.curve.valueForProgress(pos / phase.duration); + qreal rotation = from + (to - from) * value; + + QMatrix4x4 matrix; + matrix.rotate(rotation, 0, 0, 1); + transformNode->setMatrix(matrix); + } + + transformNode = static_cast<QSGTransformNode *>(transformNode->nextSibling()); + ++nodeIndex; + } +} + +void QQuickUniversalBusyIndicatorNode::sync(QQuickItem *item) +{ + QQuickUniversalBusyIndicator *indicator = static_cast<QQuickUniversalBusyIndicator *>(item); + QQuickItemPrivate *d = QQuickItemPrivate::get(item); + + QMatrix4x4 matrix; + matrix.translate(item->width() / 2, item->height() / 2); + setMatrix(matrix); + + qreal size = qMin(item->width(), item->height()); + qreal diameter = size / 10.0; + qreal radius = diameter / 2; + qreal offset = (size - diameter * 2) / M_PI; + const QRectF rect(offset, offset, diameter, diameter); + + int count = indicator->count(); + QSGNode *transformNode = firstChild(); + for (int i = 0; i < count; ++i) { + if (!transformNode) { + transformNode = new QSGTransformNode; + appendChildNode(transformNode); + + QSGOpacityNode *opacityNode = new QSGOpacityNode; + transformNode->appendChildNode(opacityNode); + + QSGInternalRectangleNode *rectNode = d->sceneGraphContext()->createInternalRectangleNode(); + rectNode->setAntialiasing(true); + opacityNode->appendChildNode(rectNode); + } + + QSGNode *opacityNode = transformNode->firstChild(); + Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType); + + QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(opacityNode->firstChild()); + Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType); + + rectNode->setRect(rect); + rectNode->setColor(indicator->color()); + rectNode->setRadius(radius); + rectNode->update(); + + transformNode = transformNode->nextSibling(); + } + + while (transformNode) { + QSGNode *nextSibling = transformNode->nextSibling(); + delete transformNode; + transformNode = nextSibling; + } +} + +QQuickUniversalBusyIndicator::QQuickUniversalBusyIndicator(QQuickItem *parent) + : QQuickItem(parent) +{ + setFlag(ItemHasContents); +} + +int QQuickUniversalBusyIndicator::count() const +{ + return m_count; +} + +void QQuickUniversalBusyIndicator::setCount(int count) +{ + if (m_count == count) + return; + + m_count = count; + update(); +} + +QColor QQuickUniversalBusyIndicator::color() const +{ + return m_color; +} + +void QQuickUniversalBusyIndicator::setColor(const QColor &color) +{ + if (m_color == color) + return; + + m_color = color; + update(); +} + +int QQuickUniversalBusyIndicator::elapsed() const +{ + return m_elapsed; +} + +void QQuickUniversalBusyIndicator::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + QQuickItem::itemChange(change, data); + if (change == ItemVisibleHasChanged) + update(); +} + +QSGNode *QQuickUniversalBusyIndicator::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + QQuickUniversalBusyIndicatorNode *node = static_cast<QQuickUniversalBusyIndicatorNode *>(oldNode); + if (isVisible() && width() > 0 && height() > 0) { + if (!node) { + node = new QQuickUniversalBusyIndicatorNode(this); + node->start(); + } + node->sync(this); + } else { + m_elapsed = node ? node->currentTime() : 0; + delete node; + node = nullptr; + } + return node; +} + +QT_END_NAMESPACE + +#include "moc_qquickuniversalbusyindicator_p.cpp" diff --git a/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h b/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h new file mode 100644 index 0000000000..1000bbab09 --- /dev/null +++ b/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h @@ -0,0 +1,57 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKUNIVERSALBUSYINDICATOR_P_H +#define QQUICKUNIVERSALBUSYINDICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qcolor.h> +#include <QtQuick/qquickitem.h> +#include <QtCore/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickUniversalBusyIndicator : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(int count READ count WRITE setCount FINAL) + Q_PROPERTY(QColor color READ color WRITE setColor FINAL) + QML_NAMED_ELEMENT(BusyIndicatorImpl) + QML_ADDED_IN_VERSION(2, 0) + +public: + explicit QQuickUniversalBusyIndicator(QQuickItem *parent = nullptr); + + int count() const; + void setCount(int count); + + QColor color() const; + void setColor(const QColor &color); + + int elapsed() const; + +protected: + void itemChange(ItemChange change, const ItemChangeData &data) override; + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override; + +private: + int m_count = 5; + int m_elapsed = 0; + QColor m_color = Qt::black; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickUniversalBusyIndicator) + +#endif // QQUICKUNIVERSALBUSYINDICATOR_P_H diff --git a/src/quickcontrols/universal/impl/qquickuniversalfocusrectangle.cpp b/src/quickcontrols/universal/impl/qquickuniversalfocusrectangle.cpp new file mode 100644 index 0000000000..9b815b9147 --- /dev/null +++ b/src/quickcontrols/universal/impl/qquickuniversalfocusrectangle.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickuniversalfocusrectangle_p.h" + +#include <QtGui/qpixmap.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpixmapcache.h> +#include <QtQuick/private/qquickitem_p.h> + +QT_BEGIN_NAMESPACE + +QQuickUniversalFocusRectangle::QQuickUniversalFocusRectangle(QQuickItem *parent) + : QQuickPaintedItem(parent) +{ + QQuickItemPrivate::get(this)->setTransparentForPositioner(true); +} + +void QQuickUniversalFocusRectangle::paint(QPainter *painter) +{ + if (!isVisible() || width() <= 0 || height() <= 0) + return; + + QRect bounds = boundingRect().toAlignedRect(); + const int boundsWidth = bounds.width(); + const int boundsHeight = bounds.width(); + const QString key = QStringLiteral("qquickuniversalfocusrectangle_%1_%2").arg(QString::number(boundsWidth), QString::number(boundsHeight)); + + QPixmap pixmap(boundsWidth, boundsHeight); + if (!QPixmapCache::find(key, &pixmap)) { + bounds.adjust(0, 0, -1, -1); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + + QPen pen; + pen.setWidth(1); + pen.setColor(Qt::white); + p.setPen(pen); + p.drawRect(bounds); + + pen.setColor(Qt::black); + pen.setDashPattern(QList<qreal>(2, 1)); + p.setPen(pen); + p.drawRect(bounds); + + QPixmapCache::insert(key, pixmap); + } + painter->drawPixmap(0, 0, pixmap); +} + +QT_END_NAMESPACE + +#include "moc_qquickuniversalfocusrectangle_p.cpp" diff --git a/src/quickcontrols/universal/impl/qquickuniversalfocusrectangle_p.h b/src/quickcontrols/universal/impl/qquickuniversalfocusrectangle_p.h new file mode 100644 index 0000000000..909c566722 --- /dev/null +++ b/src/quickcontrols/universal/impl/qquickuniversalfocusrectangle_p.h @@ -0,0 +1,37 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKUNIVERSALFOCUSRECTANGLE_P_H +#define QQUICKUNIVERSALFOCUSRECTANGLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/qquickpainteditem.h> +#include <QtCore/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickUniversalFocusRectangle : public QQuickPaintedItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(FocusRectangle) + QML_ADDED_IN_VERSION(2, 0) + +public: + QQuickUniversalFocusRectangle(QQuickItem *parent = nullptr); + + void paint(QPainter *painter) override; +}; + +QT_END_NAMESPACE + +#endif // QQUICKUNIVERSALFOCUSRECTANGLE_P_H diff --git a/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp b/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp new file mode 100644 index 0000000000..0ab47e8804 --- /dev/null +++ b/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp @@ -0,0 +1,307 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickuniversalprogressbar_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qeasingcurve.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qsgadaptationlayer_p.h> +#include <QtQuick/qsgrectanglenode.h> +#include <QtQuickControls2Impl/private/qquickanimatednode_p.h> + +QT_BEGIN_NAMESPACE + +static const int PhaseCount = 4; +static const int EllipseCount = 5; +static const int Interval = 167; +static const int TotalDuration = 3917; +static const int VisibleDuration = 3000; +static const qreal EllipseDiameter = 4; +static const qreal EllipseOffset = 4; +static const qreal ContainerAnimationStartPosition = -34; // absolute +static const qreal ContainerAnimationEndPosition = 0.435222; // relative +static const qreal EllipseAnimationWellPosition = 0.333333333333333; // relative +static const qreal EllipseAnimationEndPosition = 0.666666666666667; // relative + +class QQuickUniversalProgressBarNode : public QQuickAnimatedNode +{ +public: + QQuickUniversalProgressBarNode(QQuickUniversalProgressBar *item); + + void updateCurrentTime(int time) override; + void sync(QQuickItem *item) override; + +private: + struct Phase { + Phase() = default; + Phase(int d, qreal f, qreal t) : duration(d), from(f), to(t) { } + int duration = 0; + qreal from = 0; + qreal to = 0; + }; + + bool m_indeterminate = false; + Phase m_borderPhases[PhaseCount]; + Phase m_ellipsePhases[PhaseCount]; +}; + +QQuickUniversalProgressBarNode::QQuickUniversalProgressBarNode(QQuickUniversalProgressBar *item) + : QQuickAnimatedNode(item) +{ + setLoopCount(Infinite); + setDuration(TotalDuration); + + m_borderPhases[0] = Phase( 500, -50, 0); + m_borderPhases[1] = Phase(1500, 0, 0); + m_borderPhases[2] = Phase(1000, 0, 100); + m_borderPhases[3] = Phase( 917, 100, 100); + + m_ellipsePhases[0] = Phase(1000, 0, EllipseAnimationWellPosition); + m_ellipsePhases[1] = Phase(1000, EllipseAnimationWellPosition, EllipseAnimationWellPosition); + m_ellipsePhases[2] = Phase(1000, EllipseAnimationWellPosition, EllipseAnimationEndPosition); + m_ellipsePhases[3] = Phase(1000, EllipseAnimationWellPosition, EllipseAnimationEndPosition); +} + +void QQuickUniversalProgressBarNode::updateCurrentTime(int time) +{ + QSGRectangleNode *geometryNode = static_cast<QSGRectangleNode *>(firstChild()); + Q_ASSERT(!geometryNode || geometryNode->type() == QSGNode::GeometryNodeType); + if (!geometryNode) + return; + + QSGTransformNode *gridNode = static_cast<QSGTransformNode *>(geometryNode->firstChild()); + Q_ASSERT(!gridNode || gridNode->type() == QSGNode::TransformNodeType); + if (!gridNode) + return; + + qreal width = geometryNode->rect().width(); + { + qreal from = ContainerAnimationStartPosition; + qreal to = from + ContainerAnimationEndPosition * width; + qreal progress = static_cast<qreal>(time) / TotalDuration; + qreal dx = from + (to - from) * progress; + + QMatrix4x4 matrix; + matrix.translate(dx, 0); + gridNode->setMatrix(matrix); + } + + int nodeIndex = 0; + QSGTransformNode *borderNode = static_cast<QSGTransformNode *>(gridNode->firstChild()); + while (borderNode) { + Q_ASSERT(borderNode->type() == QSGNode::TransformNodeType); + + QSGTransformNode *ellipseNode = static_cast<QSGTransformNode *>(borderNode->firstChild()); + Q_ASSERT(ellipseNode->type() == QSGNode::TransformNodeType); + + QSGOpacityNode *opacityNode = static_cast<QSGOpacityNode *>(ellipseNode->firstChild()); + Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType); + + int begin = nodeIndex * Interval; + int end = VisibleDuration + nodeIndex * Interval; + + bool visible = time >= begin && time <= end; + opacityNode->setOpacity(visible ? 1.0 : 0.0); + + if (visible) { + { + int phaseIndex, remain = time, elapsed = 0; + for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) { + if (remain <= m_borderPhases[phaseIndex].duration + begin) + break; + remain -= m_borderPhases[phaseIndex].duration; + elapsed += m_borderPhases[phaseIndex].duration; + } + + const Phase &phase = m_borderPhases[phaseIndex]; + + qreal pos = time - elapsed - begin; + qreal progress = pos / phase.duration; + qreal dx = phase.from + (phase.to - phase.from) * progress; + + QMatrix4x4 matrix; + matrix.translate(dx, 0); + borderNode->setMatrix(matrix); + } + + { + QEasingCurve curve(QEasingCurve::BezierSpline); + curve.addCubicBezierSegment(QPointF(0.4, 0.0), QPointF(0.6, 1.0), QPointF(1.0, 1.0)); + + int phaseIndex, remain = time, elapsed = 0; + for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) { + if (remain <= m_ellipsePhases[phaseIndex].duration + begin) + break; + remain -= m_ellipsePhases[phaseIndex].duration; + elapsed += m_ellipsePhases[phaseIndex].duration; + } + + const Phase &phase = m_ellipsePhases[phaseIndex]; + + qreal from = phase.from * width; + qreal to = phase.to * width; + qreal pos = time - elapsed - begin; + qreal progress = curve.valueForProgress(pos / phase.duration); + qreal dx = from + (to - from) * progress; + + QMatrix4x4 matrix; + matrix.translate(dx, 0); + ellipseNode->setMatrix(matrix); + } + } + + borderNode = static_cast<QSGTransformNode *>(borderNode->nextSibling()); + ++nodeIndex; + } +} + +void QQuickUniversalProgressBarNode::sync(QQuickItem *item) +{ + QQuickUniversalProgressBar *bar = static_cast<QQuickUniversalProgressBar *>(item); + if (m_indeterminate != bar->isIndeterminate()) { + m_indeterminate = bar->isIndeterminate(); + if (m_indeterminate) + start(); + else + stop(); + } + + QQuickItemPrivate *d = QQuickItemPrivate::get(item); + + QRectF bounds = item->boundingRect(); + bounds.setHeight(item->implicitHeight()); + bounds.moveTop((item->height() - bounds.height()) / 2.0); + if (!m_indeterminate) + bounds.setWidth(bar->progress() * bounds.width()); + + QSGRectangleNode *geometryNode = static_cast<QSGRectangleNode *>(firstChild()); + if (!geometryNode) { + geometryNode = item->window()->createRectangleNode(); + appendChildNode(geometryNode); + } + geometryNode->setRect(bounds); + geometryNode->setColor(m_indeterminate ? Qt::transparent : bar->color()); + + if (!m_indeterminate) { + while (QSGNode *node = geometryNode->firstChild()) + delete node; + return; + } + + QSGTransformNode *gridNode = static_cast<QSGTransformNode *>(geometryNode->firstChild()); + if (!gridNode) { + gridNode = new QSGTransformNode; + geometryNode->appendChildNode(gridNode); + } + Q_ASSERT(gridNode->type() == QSGNode::TransformNodeType); + + QSGNode *borderNode = gridNode->firstChild(); + for (int i = 0; i < EllipseCount; ++i) { + if (!borderNode) { + borderNode = new QSGTransformNode; + gridNode->appendChildNode(borderNode); + + QSGTransformNode *ellipseNode = new QSGTransformNode; + borderNode->appendChildNode(ellipseNode); + + QSGOpacityNode *opacityNode = new QSGOpacityNode; + ellipseNode->appendChildNode(opacityNode); + + QSGInternalRectangleNode *rectNode = d->sceneGraphContext()->createInternalRectangleNode(); + rectNode->setAntialiasing(true); + rectNode->setRadius(EllipseDiameter / 2); + opacityNode->appendChildNode(rectNode); + } + Q_ASSERT(borderNode->type() == QSGNode::TransformNodeType); + + QSGNode *ellipseNode = borderNode->firstChild(); + Q_ASSERT(ellipseNode->type() == QSGNode::TransformNodeType); + + QSGNode *opacityNode = ellipseNode->firstChild(); + Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType); + + QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(opacityNode->firstChild()); + Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType); + + rectNode->setRect(QRectF((EllipseCount - i - 1) * (EllipseDiameter + EllipseOffset), (item->height() - EllipseDiameter) / 2, EllipseDiameter, EllipseDiameter)); + rectNode->setColor(bar->color()); + rectNode->update(); + + borderNode = borderNode->nextSibling(); + } +} + +QQuickUniversalProgressBar::QQuickUniversalProgressBar(QQuickItem *parent) + : QQuickItem(parent) +{ + setFlag(ItemHasContents); +} + +QColor QQuickUniversalProgressBar::color() const +{ + return m_color; +} + +void QQuickUniversalProgressBar::setColor(const QColor &color) +{ + if (m_color == color) + return; + + m_color = color; + update(); +} + +qreal QQuickUniversalProgressBar::progress() const +{ + return m_progress; +} + +void QQuickUniversalProgressBar::setProgress(qreal progress) +{ + if (progress == m_progress) + return; + + m_progress = progress; + update(); +} + +bool QQuickUniversalProgressBar::isIndeterminate() const +{ + return m_indeterminate; +} + +void QQuickUniversalProgressBar::setIndeterminate(bool indeterminate) +{ + if (indeterminate == m_indeterminate) + return; + + m_indeterminate = indeterminate; + setClip(m_indeterminate); + update(); +} + +void QQuickUniversalProgressBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + QQuickItem::itemChange(change, data); + if (change == ItemVisibleHasChanged) + update(); +} + +QSGNode *QQuickUniversalProgressBar::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) +{ + QQuickUniversalProgressBarNode *node = static_cast<QQuickUniversalProgressBarNode *>(oldNode); + if (isVisible() && width() > 0 && height() > 0) { + if (!node) + node = new QQuickUniversalProgressBarNode(this); + node->sync(this); + } else { + delete node; + node = nullptr; + } + return node; +} + +QT_END_NAMESPACE + +#include "moc_qquickuniversalprogressbar_p.cpp" diff --git a/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h b/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h new file mode 100644 index 0000000000..a4bbae8cb5 --- /dev/null +++ b/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h @@ -0,0 +1,59 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKUNIVERSALPROGRESSBAR_P_H +#define QQUICKUNIVERSALPROGRESSBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qcolor.h> +#include <QtQuick/qquickitem.h> +#include <QtCore/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickUniversalProgressBar : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor FINAL) + Q_PROPERTY(qreal progress READ progress WRITE setProgress FINAL) + Q_PROPERTY(bool indeterminate READ isIndeterminate WRITE setIndeterminate FINAL) + QML_NAMED_ELEMENT(ProgressBarImpl) + QML_ADDED_IN_VERSION(2, 0) + +public: + explicit QQuickUniversalProgressBar(QQuickItem *parent = nullptr); + + QColor color() const; + void setColor(const QColor &color); + + qreal progress() const; + void setProgress(qreal progress); + + bool isIndeterminate() const; + void setIndeterminate(bool indeterminate); + +protected: + void itemChange(ItemChange change, const ItemChangeData &data) override; + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override; + +private: + QColor m_color = Qt::black; + qreal m_progress = 0.0; + bool m_indeterminate = false; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickUniversalProgressBar) + +#endif // QQUICKUNIVERSALPROGRESSBAR_P_H |