plugin/managers: add CircularIndicatorManager
Basically move StyledBusyIndicator -> CircularIndicator + logic for it from js -> c++
This commit is contained in:
parent
306cfc06ed
commit
98a709af78
10 changed files with 400 additions and 162 deletions
108
components/controls/CircularIndicator.qml
Normal file
108
components/controls/CircularIndicator.qml
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
import ".."
|
||||||
|
import qs.services
|
||||||
|
import qs.config
|
||||||
|
import Caelestia.Managers
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum AnimType {
|
||||||
|
Advance = 0,
|
||||||
|
Retreat
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AnimState {
|
||||||
|
Stopped,
|
||||||
|
Running,
|
||||||
|
Completing
|
||||||
|
}
|
||||||
|
|
||||||
|
property real implicitSize: Appearance.font.size.normal * 3
|
||||||
|
property real strokeWidth: Appearance.padding.small * 0.8
|
||||||
|
property color fgColour: Colours.palette.m3primary
|
||||||
|
property color bgColour: Colours.palette.m3secondaryContainer
|
||||||
|
|
||||||
|
property alias type: manager.indeterminateAnimationType
|
||||||
|
readonly property alias progress: manager.progress
|
||||||
|
|
||||||
|
property real internalStrokeWidth: strokeWidth
|
||||||
|
property int animState
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (running) {
|
||||||
|
running = false;
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if (running) {
|
||||||
|
manager.completeEndProgress = 0;
|
||||||
|
animState = CircularIndicator.Running;
|
||||||
|
} else {
|
||||||
|
if (animState == CircularIndicator.Running)
|
||||||
|
animState = CircularIndicator.Completing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "stopped"
|
||||||
|
when: !root.running
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
root.opacity: 0
|
||||||
|
root.internalStrokeWidth: root.strokeWidth / 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitions: Transition {
|
||||||
|
Anim {
|
||||||
|
properties: "opacity,internalStrokeWidth"
|
||||||
|
duration: manager.completeEndDuration * Appearance.anim.durations.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: CircularProgress {
|
||||||
|
anchors.fill: parent
|
||||||
|
strokeWidth: root.internalStrokeWidth
|
||||||
|
fgColour: root.fgColour
|
||||||
|
bgColour: root.bgColour
|
||||||
|
padding: root.padding
|
||||||
|
rotation: manager.rotation
|
||||||
|
startAngle: manager.startFraction * 360
|
||||||
|
value: manager.endFraction - manager.startFraction
|
||||||
|
}
|
||||||
|
|
||||||
|
CircularIndicatorManager {
|
||||||
|
id: manager
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
running: root.animState !== CircularIndicator.Stopped
|
||||||
|
loops: Animation.Infinite
|
||||||
|
target: manager
|
||||||
|
property: "progress"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: manager.duration * Appearance.anim.durations.scale
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
running: root.animState === CircularIndicator.Completing
|
||||||
|
target: manager
|
||||||
|
property: "completeEndProgress"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: manager.completeEndDuration * Appearance.anim.durations.scale
|
||||||
|
onFinished: {
|
||||||
|
if (root.animState === CircularIndicator.Completing)
|
||||||
|
root.animState = CircularIndicator.Stopped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import ".."
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.config
|
import qs.config
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
@ -38,10 +39,8 @@ Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on strokeColor {
|
Behavior on strokeColor {
|
||||||
ColorAnimation {
|
CAnim {
|
||||||
duration: Appearance.anim.durations.large
|
duration: Appearance.anim.durations.large
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,10 +61,8 @@ Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on strokeColor {
|
Behavior on strokeColor {
|
||||||
ColorAnimation {
|
CAnim {
|
||||||
duration: Appearance.anim.durations.large
|
duration: Appearance.anim.durations.large
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
import ".."
|
|
||||||
import qs.services
|
|
||||||
import qs.config
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
BusyIndicator {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property real implicitSize: Appearance.font.size.normal * 3
|
|
||||||
property real strokeWidth: Appearance.padding.small
|
|
||||||
property color fgColour: Colours.palette.m3primary
|
|
||||||
property color bgColour: Colours.palette.m3secondaryContainer
|
|
||||||
|
|
||||||
property real internalStrokeWidth: strokeWidth
|
|
||||||
property string animState
|
|
||||||
|
|
||||||
padding: 0
|
|
||||||
implicitWidth: implicitSize
|
|
||||||
implicitHeight: implicitSize
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (running) {
|
|
||||||
updater.completeEndProgress = 0;
|
|
||||||
animState = "running";
|
|
||||||
} else {
|
|
||||||
if (animState == "running")
|
|
||||||
animState = "completing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
states: State {
|
|
||||||
name: "stopped"
|
|
||||||
when: !root.running
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.opacity: 0
|
|
||||||
root.internalStrokeWidth: root.strokeWidth / 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transitions: Transition {
|
|
||||||
Anim {
|
|
||||||
properties: "opacity,internalStrokeWidth"
|
|
||||||
duration: updater.completeEndDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
background: null
|
|
||||||
|
|
||||||
contentItem: CircularProgress {
|
|
||||||
anchors.fill: parent
|
|
||||||
strokeWidth: root.internalStrokeWidth
|
|
||||||
fgColour: root.fgColour
|
|
||||||
bgColour: root.bgColour
|
|
||||||
padding: root.padding
|
|
||||||
startAngle: updater.startFraction * 360
|
|
||||||
value: updater.endFraction - updater.startFraction
|
|
||||||
}
|
|
||||||
|
|
||||||
Updater {
|
|
||||||
id: updater
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
running: root.animState !== "stopped"
|
|
||||||
loops: Animation.Infinite
|
|
||||||
target: updater
|
|
||||||
property: "progress"
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
duration: updater.duration
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
running: root.animState === "completing"
|
|
||||||
target: updater
|
|
||||||
property: "completeEndProgress"
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
duration: updater.completeEndDuration
|
|
||||||
onFinished: {
|
|
||||||
if (root.animState === "completing")
|
|
||||||
root.animState = "stopped";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component Updater: QtObject {
|
|
||||||
readonly property int duration: 5400 * Appearance.anim.durations.scale
|
|
||||||
readonly property int expandDuration: 667 * Appearance.anim.durations.scale
|
|
||||||
readonly property int collapseDuration: 667 * Appearance.anim.durations.scale
|
|
||||||
readonly property int completeEndDuration: 333 * Appearance.anim.durations.scale
|
|
||||||
readonly property int tailDegOffset: -20
|
|
||||||
readonly property int extraDegPerCycle: 250
|
|
||||||
readonly property int constantRotDeg: 1520
|
|
||||||
readonly property list<int> expandDelay: [0, 1350, 2700, 4050].map(d => d * Appearance.anim.durations.scale)
|
|
||||||
readonly property list<int> collapseDelay: [667, 2017, 3367, 4717].map(d => d * Appearance.anim.durations.scale)
|
|
||||||
|
|
||||||
property real progress: 0
|
|
||||||
property real startFraction: 0
|
|
||||||
property real endFraction: 0
|
|
||||||
property real rotation: 0
|
|
||||||
property real completeEndProgress: 0
|
|
||||||
|
|
||||||
onProgressChanged: update(progress)
|
|
||||||
|
|
||||||
function update(p: real): void {
|
|
||||||
const playtime = p * duration;
|
|
||||||
let startDeg = constantRotDeg * p + tailDegOffset;
|
|
||||||
let endDeg = constantRotDeg * p;
|
|
||||||
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
const expandFraction = getFractionInRange(playtime, expandDelay[i], expandDuration);
|
|
||||||
endDeg += fastOutSlowIn(expandFraction) * extraDegPerCycle;
|
|
||||||
|
|
||||||
const collapseFraction = getFractionInRange(playtime, collapseDelay[i], collapseDuration);
|
|
||||||
startDeg += fastOutSlowIn(collapseFraction) * extraDegPerCycle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gap closing
|
|
||||||
startDeg += (endDeg - startDeg) * completeEndProgress;
|
|
||||||
|
|
||||||
startFraction = startDeg / 360;
|
|
||||||
endFraction = endDeg / 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFractionInRange(currentTime: real, delay: int, duration: int): real {
|
|
||||||
if (currentTime < delay)
|
|
||||||
return 0;
|
|
||||||
if (currentTime > delay + duration)
|
|
||||||
return 1;
|
|
||||||
return (currentTime - delay) / duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lerp(a: real, b: real, t: real): real {
|
|
||||||
return a + (b - a) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
function cubic(a: real, b: real, c: real, d: real, t: real): real {
|
|
||||||
return ((1 - t) ** 3) * a + 3 * ((1 - t) ** 2) * t * b + 3 * (1 - t) * (t ** 2) * c + (t ** 3) * d;
|
|
||||||
}
|
|
||||||
|
|
||||||
function cubicBezier(p1x: real, p1y: real, p2x: real, p2y: real, t: real): real {
|
|
||||||
return cubic(0, p1y, p2y, 1, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fastOutSlowIn(t: real): real {
|
|
||||||
return cubicBezier(0.4, 0.0, 0.2, 1.0, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -110,7 +110,7 @@ ColumnLayout {
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Qt.alpha(Colours.palette.m3primary, device.modelData.state === BluetoothDeviceState.Connected ? 1 : 0)
|
color: Qt.alpha(Colours.palette.m3primary, device.modelData.state === BluetoothDeviceState.Connected ? 1 : 0)
|
||||||
|
|
||||||
StyledBusyIndicator {
|
CircularIndicator {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
running: device.loading
|
running: device.loading
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ ColumnLayout {
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Qt.alpha(Colours.palette.m3primary, networkItem.modelData.active ? 1 : 0)
|
color: Qt.alpha(Colours.palette.m3primary, networkItem.modelData.active ? 1 : 0)
|
||||||
|
|
||||||
StyledBusyIndicator {
|
CircularIndicator {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
running: networkItem.loading
|
running: networkItem.loading
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +183,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledBusyIndicator {
|
CircularIndicator {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
strokeWidth: Appearance.padding.small / 2
|
strokeWidth: Appearance.padding.small / 2
|
||||||
bgColour: "transparent"
|
bgColour: "transparent"
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ ColumnLayout {
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Qt.alpha(Colours.palette.m3primaryContainer, device.connected ? 1 : 0)
|
color: Qt.alpha(Colours.palette.m3primaryContainer, device.connected ? 1 : 0)
|
||||||
|
|
||||||
StyledBusyIndicator {
|
CircularIndicator {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
running: device.loading
|
running: device.loading
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledBusyIndicator {
|
CircularIndicator {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
running: root.lock.pam.passwd.active
|
running: root.lock.pam.passwd.active
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ qml_module(caelestia-managers
|
||||||
URI Caelestia.Managers
|
URI Caelestia.Managers
|
||||||
SOURCES
|
SOURCES
|
||||||
cachingimagemanager.hpp cachingimagemanager.cpp
|
cachingimagemanager.hpp cachingimagemanager.cpp
|
||||||
|
circularindicatormanager.hpp circularindicatormanager.cpp
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
Qt::Gui
|
Qt::Gui
|
||||||
Qt::Quick
|
Qt::Quick
|
||||||
|
|
|
||||||
211
plugin/src/Caelestia/Managers/circularindicatormanager.cpp
Normal file
211
plugin/src/Caelestia/Managers/circularindicatormanager.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
#include "circularindicatormanager.hpp"
|
||||||
|
#include <qeasingcurve.h>
|
||||||
|
#include <qpoint.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
namespace advance {
|
||||||
|
|
||||||
|
constexpr qint32 TOTAL_CYCLES = 4;
|
||||||
|
constexpr qint32 TOTAL_DURATION_IN_MS = 5400;
|
||||||
|
constexpr qint32 DURATION_TO_EXPAND_IN_MS = 667;
|
||||||
|
constexpr qint32 DURATION_TO_COLLAPSE_IN_MS = 667;
|
||||||
|
constexpr qint32 DURATION_TO_COMPLETE_END_IN_MS = 333;
|
||||||
|
constexpr qint32 TAIL_DEGREES_OFFSET = -20;
|
||||||
|
constexpr qint32 EXTRA_DEGREES_PER_CYCLE = 250;
|
||||||
|
constexpr qint32 CONSTANT_ROTATION_DEGREES = 1520;
|
||||||
|
|
||||||
|
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_EXPAND_IN_MS = { 0, 1350, 2700, 4050 };
|
||||||
|
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_COLLAPSE_IN_MS = { 667, 2017, 3367, 4717 };
|
||||||
|
|
||||||
|
} // namespace advance
|
||||||
|
|
||||||
|
namespace retreat {
|
||||||
|
|
||||||
|
constexpr qint32 TOTAL_DURATION_IN_MS = 6000;
|
||||||
|
constexpr qint32 DURATION_SPIN_IN_MS = 500;
|
||||||
|
constexpr qint32 DURATION_GROW_ACTIVE_IN_MS = 3000;
|
||||||
|
constexpr qint32 DURATION_SHRINK_ACTIVE_IN_MS = 3000;
|
||||||
|
constexpr std::array DELAY_SPINS_IN_MS = { 0, 1500, 3000, 4500 };
|
||||||
|
constexpr qint32 DELAY_GROW_ACTIVE_IN_MS = 0;
|
||||||
|
constexpr qint32 DELAY_SHRINK_ACTIVE_IN_MS = 3000;
|
||||||
|
constexpr qint32 DURATION_TO_COMPLETE_END_IN_MS = 500;
|
||||||
|
|
||||||
|
// Constants for animation values.
|
||||||
|
|
||||||
|
// The total degrees that a constant rotation goes by.
|
||||||
|
constexpr qint32 CONSTANT_ROTATION_DEGREES = 1080;
|
||||||
|
// Despite of the constant rotation, there are also 5 extra rotations the entire animation. The
|
||||||
|
// total degrees that each extra rotation goes by.
|
||||||
|
constexpr qint32 SPIN_ROTATION_DEGREES = 90;
|
||||||
|
constexpr std::array<qreal, 2> END_FRACTION_RANGE = { 0.10, 0.87 };
|
||||||
|
|
||||||
|
} // namespace retreat
|
||||||
|
|
||||||
|
inline qreal getFractionInRange(qreal playtime, qreal start, qreal duration) {
|
||||||
|
const auto fraction = (playtime - start) / duration;
|
||||||
|
return std::clamp(fraction, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace caelestia {
|
||||||
|
|
||||||
|
CircularIndicatorManager::CircularIndicatorManager(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_type(IndeterminateAnimationType::Advance)
|
||||||
|
, m_curve(QEasingCurve(QEasingCurve::BezierSpline))
|
||||||
|
, m_progress(0)
|
||||||
|
, m_startFraction(0)
|
||||||
|
, m_endFraction(0)
|
||||||
|
, m_rotation(0)
|
||||||
|
, m_completeEndProgress(0) {
|
||||||
|
// Fast out slow in
|
||||||
|
m_curve.addCubicBezierSegment({ 0.4, 0.0 }, { 0.2, 1.0 }, { 1.0, 1.0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::startFraction() const {
|
||||||
|
return m_startFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::endFraction() const {
|
||||||
|
return m_endFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::rotation() const {
|
||||||
|
return m_rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::progress() const {
|
||||||
|
return m_progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CircularIndicatorManager::setProgress(qreal progress) {
|
||||||
|
update(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::duration() const {
|
||||||
|
if (m_type == IndeterminateAnimationType::Advance) {
|
||||||
|
return advance::TOTAL_DURATION_IN_MS;
|
||||||
|
} else {
|
||||||
|
return retreat::TOTAL_DURATION_IN_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::completeEndDuration() const {
|
||||||
|
if (m_type == IndeterminateAnimationType::Advance) {
|
||||||
|
return advance::DURATION_TO_COMPLETE_END_IN_MS;
|
||||||
|
} else {
|
||||||
|
return retreat::DURATION_TO_COMPLETE_END_IN_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CircularIndicatorManager::IndeterminateAnimationType CircularIndicatorManager::indeterminateAnimationType() const {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CircularIndicatorManager::setIndeterminateAnimationType(IndeterminateAnimationType t) {
|
||||||
|
if (m_type != t) {
|
||||||
|
m_type = t;
|
||||||
|
emit indeterminateAnimationTypeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CircularIndicatorManager::completeEndProgress() const {
|
||||||
|
return m_completeEndProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CircularIndicatorManager::setCompleteEndProgress(qreal progress) {
|
||||||
|
if (qFuzzyCompare(m_completeEndProgress + 1.0, progress + 1.0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_completeEndProgress = progress;
|
||||||
|
emit completeEndProgressChanged();
|
||||||
|
|
||||||
|
update(m_progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CircularIndicatorManager::update(qreal progress) {
|
||||||
|
if (qFuzzyCompare(m_progress + 1.0, progress + 1.0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_type == IndeterminateAnimationType::Advance) {
|
||||||
|
updateAdvance(progress);
|
||||||
|
} else {
|
||||||
|
updateRetreat(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_progress = progress;
|
||||||
|
emit progressChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CircularIndicatorManager::updateRetreat(qreal progress) {
|
||||||
|
using namespace retreat;
|
||||||
|
const auto playtime = progress * TOTAL_DURATION_IN_MS;
|
||||||
|
|
||||||
|
// Constant rotation.
|
||||||
|
const qreal constantRotation = CONSTANT_ROTATION_DEGREES * progress;
|
||||||
|
// Extra rotation for the faster spinning.
|
||||||
|
qreal spinRotation = 0;
|
||||||
|
for (const int spinDelay : DELAY_SPINS_IN_MS) {
|
||||||
|
spinRotation += m_curve.valueForProgress(getFractionInRange(playtime, spinDelay, DURATION_SPIN_IN_MS)) *
|
||||||
|
SPIN_ROTATION_DEGREES;
|
||||||
|
}
|
||||||
|
m_rotation = constantRotation + spinRotation;
|
||||||
|
emit rotationChanged();
|
||||||
|
|
||||||
|
// Grow active indicator.
|
||||||
|
qreal fraction =
|
||||||
|
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS));
|
||||||
|
fraction -=
|
||||||
|
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS));
|
||||||
|
|
||||||
|
if (!qFuzzyIsNull(m_startFraction)) {
|
||||||
|
m_startFraction = 0.0;
|
||||||
|
emit startFractionChanged();
|
||||||
|
}
|
||||||
|
const auto oldEndFrac = m_endFraction;
|
||||||
|
m_endFraction = std::lerp(END_FRACTION_RANGE[0], END_FRACTION_RANGE[1], fraction);
|
||||||
|
|
||||||
|
// Completing animation.
|
||||||
|
if (m_completeEndProgress > 0) {
|
||||||
|
m_endFraction *= 1 - m_completeEndProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qFuzzyCompare(m_endFraction + 1.0, oldEndFrac + 1.0)) {
|
||||||
|
emit endFractionChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CircularIndicatorManager::updateAdvance(qreal progress) {
|
||||||
|
using namespace advance;
|
||||||
|
const auto playtime = progress * TOTAL_DURATION_IN_MS;
|
||||||
|
|
||||||
|
// Adds constant rotation to segment positions.
|
||||||
|
m_startFraction = CONSTANT_ROTATION_DEGREES * progress + TAIL_DEGREES_OFFSET;
|
||||||
|
m_endFraction = CONSTANT_ROTATION_DEGREES * progress;
|
||||||
|
|
||||||
|
// Adds cycle specific rotation to segment positions.
|
||||||
|
for (size_t cycleIndex = 0; cycleIndex < TOTAL_CYCLES; ++cycleIndex) {
|
||||||
|
// While expanding.
|
||||||
|
qreal fraction = getFractionInRange(playtime, DELAY_TO_EXPAND_IN_MS[cycleIndex], DURATION_TO_EXPAND_IN_MS);
|
||||||
|
m_endFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
|
||||||
|
|
||||||
|
// While collapsing.
|
||||||
|
fraction = getFractionInRange(playtime, DELAY_TO_COLLAPSE_IN_MS[cycleIndex], DURATION_TO_COLLAPSE_IN_MS);
|
||||||
|
m_startFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes the gap between head and tail for complete end.
|
||||||
|
m_startFraction += (m_endFraction - m_startFraction) * m_completeEndProgress;
|
||||||
|
|
||||||
|
m_startFraction /= 360.0;
|
||||||
|
m_endFraction /= 360.0;
|
||||||
|
|
||||||
|
emit startFractionChanged();
|
||||||
|
emit endFractionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace caelestia
|
||||||
72
plugin/src/Caelestia/Managers/circularindicatormanager.hpp
Normal file
72
plugin/src/Caelestia/Managers/circularindicatormanager.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qeasingcurve.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
|
||||||
|
namespace caelestia {
|
||||||
|
|
||||||
|
class CircularIndicatorManager : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY startFractionChanged)
|
||||||
|
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged)
|
||||||
|
Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged)
|
||||||
|
Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
|
||||||
|
Q_PROPERTY(qreal completeEndProgress READ completeEndProgress WRITE setCompleteEndProgress NOTIFY
|
||||||
|
completeEndProgressChanged)
|
||||||
|
Q_PROPERTY(qreal duration READ duration NOTIFY indeterminateAnimationTypeChanged)
|
||||||
|
Q_PROPERTY(qreal completeEndDuration READ completeEndDuration NOTIFY indeterminateAnimationTypeChanged)
|
||||||
|
Q_PROPERTY(IndeterminateAnimationType indeterminateAnimationType READ indeterminateAnimationType WRITE
|
||||||
|
setIndeterminateAnimationType NOTIFY indeterminateAnimationTypeChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CircularIndicatorManager(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
enum IndeterminateAnimationType {
|
||||||
|
Advance = 0,
|
||||||
|
Retreat
|
||||||
|
};
|
||||||
|
Q_ENUM(IndeterminateAnimationType)
|
||||||
|
|
||||||
|
[[nodiscard]] qreal startFraction() const;
|
||||||
|
[[nodiscard]] qreal endFraction() const;
|
||||||
|
[[nodiscard]] qreal rotation() const;
|
||||||
|
|
||||||
|
[[nodiscard]] qreal progress() const;
|
||||||
|
void setProgress(qreal progress);
|
||||||
|
|
||||||
|
[[nodiscard]] qreal completeEndProgress() const;
|
||||||
|
void setCompleteEndProgress(qreal progress);
|
||||||
|
|
||||||
|
[[nodiscard]] qreal duration() const;
|
||||||
|
[[nodiscard]] qreal completeEndDuration() const;
|
||||||
|
|
||||||
|
[[nodiscard]] IndeterminateAnimationType indeterminateAnimationType() const;
|
||||||
|
void setIndeterminateAnimationType(IndeterminateAnimationType t);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startFractionChanged();
|
||||||
|
void endFractionChanged();
|
||||||
|
void rotationChanged();
|
||||||
|
void progressChanged();
|
||||||
|
void completeEndProgressChanged();
|
||||||
|
void indeterminateAnimationTypeChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IndeterminateAnimationType m_type;
|
||||||
|
QEasingCurve m_curve;
|
||||||
|
|
||||||
|
qreal m_progress;
|
||||||
|
qreal m_startFraction;
|
||||||
|
qreal m_endFraction;
|
||||||
|
qreal m_rotation;
|
||||||
|
qreal m_completeEndProgress;
|
||||||
|
|
||||||
|
void update(qreal progress);
|
||||||
|
void updateAdvance(qreal progress);
|
||||||
|
void updateRetreat(qreal progress);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace caelestia
|
||||||
Loading…
Add table
Reference in a new issue