12#include <QJsonDocument>
33template <
typename StepType>
39 using FillStep = void (*)(
const QString &
id,
const QJsonObject &stepObj, StepType &step);
57 : m_fillStep(fillStep)
58 , m_persistence(persistence)
71 m_resourcePath = resourcePath;
73 QFile file(resourcePath);
74 if (!file.open(QIODevice::ReadOnly)) {
78 const QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
79 if (doc.isNull() || !doc.isObject()) {
83 const QJsonObject root = doc.object();
84 m_id = root.value(
"id").toString();
85 const QString rawTitle = root.value(
"title").toString();
87 if (m_id.isEmpty() || rawTitle.isEmpty()) {
93 const QJsonArray stepsArray = root.value(
"steps").toArray();
94 if (stepsArray.isEmpty()) {
98 for (
const QJsonValue &stepVal : stepsArray) {
100 m_fillStep(m_id, stepVal.toObject(), step);
101 m_steps.append(step);
112 if (m_steps.isEmpty()) {
115 m_currentStep = m_persistence.getProgress(m_id);
116 if (m_currentStep < 0 || m_currentStep >= m_steps.size()) {
138 if (!m_active || m_currentStep <= 0) {
142 m_persistence.setProgress(m_id, m_currentStep);
150 if (!m_active || m_steps.isEmpty()) {
153 if (m_currentStep >= m_steps.size() - 1) {
154 return {.reachedEnd =
true};
156 const int completed = m_currentStep;
158 m_persistence.setProgress(m_id, m_currentStep);
159 return {.advanced =
true, .completedStep = completed};
167 if (m_resourcePath.isEmpty() || !m_active) {
170 const int savedStep = m_currentStep;
176 m_currentStep = qBound(0, savedStep,
static_cast<int>(m_steps.size()) - 1);
187 QStringList completed = m_persistence.getCompleted();
188 if (!completed.contains(m_id)) {
189 completed.append(m_id);
190 m_persistence.setCompleted(completed);
192 m_persistence.setProgress(m_id, 0);
197 static StepType empty;
198 if (m_steps.isEmpty() || m_currentStep < 0 || m_currentStep >= m_steps.size()) {
201 return m_steps.at(m_currentStep);
204 QString
id()
const {
return m_id; }
205 QString
title()
const {
return m_title; }
207 int totalSteps()
const {
return static_cast<int>(m_steps.size()); }
212 Persistence m_persistence;
216 QString m_resourcePath;
217 QVector<StepType> m_steps;
218 int m_currentStep = 0;
219 bool m_active =
false;
Discovery and translation lookup for Exercise/Tour step content.
static QString translate(const QString &key, const QString &fallbackEnglish)
StepEngineCore(FillStep fillStep, Persistence persistence)
void(*)(const QString &id, const QJsonObject &stepObj, StepType &step) FillStep
bool loadFromResource(const QString &resourcePath)
const StepType & currentStepData() const
Result of advance(): tells the owning engine what to emit next.
int completedStep
the step index that just finished; valid when advanced is true.
bool reachedEnd
call the caller's markCompleted().
bool advanced
emit stepChanged() (and stepCompleted(), if the caller has one).
The four Settings::* functions each engine persists progress/completion through.
QStringList(* getCompleted)()
void(* setCompleted)(const QStringList &)
int(* getProgress)(const QString &)
void(* setProgress)(const QString &, int)