wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
StepEngineCore.h
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
7
8#pragma once
9
10#include <QFile>
11#include <QJsonArray>
12#include <QJsonDocument>
13#include <QJsonObject>
14#include <QString>
15#include <QVector>
16
18
33template <typename StepType>
35{
36public:
39 using FillStep = void (*)(const QString &id, const QJsonObject &stepObj, StepType &step);
40
42 struct Persistence {
43 int (*getProgress)(const QString &);
44 void (*setProgress)(const QString &, int);
45 QStringList (*getCompleted)();
46 void (*setCompleted)(const QStringList &);
47 };
48
51 bool advanced = false;
52 bool reachedEnd = false;
53 int completedStep = -1;
54 };
55
56 StepEngineCore(FillStep fillStep, Persistence persistence)
57 : m_fillStep(fillStep)
58 , m_persistence(persistence)
59 {
60 }
61
64 bool loadFromResource(const QString &resourcePath)
65 {
66 m_active = false;
67 m_steps.clear();
68 m_currentStep = 0;
69 m_id.clear();
70 m_title.clear();
71 m_resourcePath = resourcePath;
72
73 QFile file(resourcePath);
74 if (!file.open(QIODevice::ReadOnly)) {
75 return false;
76 }
77
78 const QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
79 if (doc.isNull() || !doc.isObject()) {
80 return false;
81 }
82
83 const QJsonObject root = doc.object();
84 m_id = root.value("id").toString();
85 const QString rawTitle = root.value("title").toString();
86
87 if (m_id.isEmpty() || rawTitle.isEmpty()) {
88 return false;
89 }
90
91 m_title = ExerciseTourResources::translate(m_id + QStringLiteral(".title"), rawTitle);
92
93 const QJsonArray stepsArray = root.value("steps").toArray();
94 if (stepsArray.isEmpty()) {
95 return false;
96 }
97
98 for (const QJsonValue &stepVal : stepsArray) {
99 StepType step;
100 m_fillStep(m_id, stepVal.toObject(), step);
101 m_steps.append(step);
102 }
103
104 return true;
105 }
106
110 bool start()
111 {
112 if (m_steps.isEmpty()) {
113 return false;
114 }
115 m_currentStep = m_persistence.getProgress(m_id);
116 if (m_currentStep < 0 || m_currentStep >= m_steps.size()) {
117 m_currentStep = 0;
118 }
119 m_active = true;
120 return true;
121 }
122
125 bool stop()
126 {
127 if (!m_active) {
128 return false;
129 }
130 m_active = false;
131 return true;
132 }
133
137 {
138 if (!m_active || m_currentStep <= 0) {
139 return false;
140 }
141 --m_currentStep;
142 m_persistence.setProgress(m_id, m_currentStep);
143 return true;
144 }
145
149 {
150 if (!m_active || m_steps.isEmpty()) {
151 return {};
152 }
153 if (m_currentStep >= m_steps.size() - 1) {
154 return {.reachedEnd = true};
155 }
156 const int completed = m_currentStep;
157 ++m_currentStep;
158 m_persistence.setProgress(m_id, m_currentStep);
159 return {.advanced = true, .completedStep = completed};
160 }
161
166 {
167 if (m_resourcePath.isEmpty() || !m_active) {
168 return false;
169 }
170 const int savedStep = m_currentStep;
171
172 if (!loadFromResource(m_resourcePath)) {
173 return false;
174 }
175
176 m_currentStep = qBound(0, savedStep, static_cast<int>(m_steps.size()) - 1);
177 m_active = true;
178 return true;
179 }
180
184 {
185 m_active = false;
186
187 QStringList completed = m_persistence.getCompleted();
188 if (!completed.contains(m_id)) {
189 completed.append(m_id);
190 m_persistence.setCompleted(completed);
191 }
192 m_persistence.setProgress(m_id, 0);
193 }
194
195 const StepType &currentStepData() const
196 {
197 static StepType empty;
198 if (m_steps.isEmpty() || m_currentStep < 0 || m_currentStep >= m_steps.size()) {
199 return empty;
200 }
201 return m_steps.at(m_currentStep);
202 }
203
204 QString id() const { return m_id; }
205 QString title() const { return m_title; }
206 int currentStep() const { return m_currentStep; }
207 int totalSteps() const { return static_cast<int>(m_steps.size()); }
208 bool isActive() const { return m_active; }
209
210private:
211 FillStep m_fillStep;
212 Persistence m_persistence;
213
214 QString m_id;
215 QString m_title;
216 QString m_resourcePath;
217 QVector<StepType> m_steps;
218 int m_currentStep = 0;
219 bool m_active = false;
220};
Discovery and translation lookup for Exercise/Tour step content.
static QString translate(const QString &key, const QString &fallbackEnglish)
AdvanceResult advance()
int totalSteps() const
QString title() const
bool isActive() const
StepEngineCore(FillStep fillStep, Persistence persistence)
int currentStep() const
QString id() const
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)