[education/minuet] src/app: Let the user decide about tests' next question autoplay (default=false)
Sandro S. Andrade <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 297b80c86539624f99bcc643c99a7df0eee9ba97 by Sandro S. Andrade.
Committed on 10/08/2026 at 19:34.
Pushed by sandroandrade into branch 'master'.
Let the user decide about tests' next question autoplay (default=false)
M +28 -0 src/app/autotests/settingscontrollertest.cpp
M +2 -2 src/app/qml/exercises/ExerciseClappingView.qml
M +2 -2 src/app/qml/exercises/ExerciseSingingView.qml
M +5 -4 src/app/qml/exercises/ExerciseView.qml
M +7 -1 src/app/qml/pages/SettingsPage.qml
M +17 -0 src/app/settingscontroller.cpp
M +6 -0 src/app/settingscontroller.h
https://invent.kde.org/education/minuet/-/commit/297b80c86539624f99bcc643c99a7df0eee9ba97
diff --git a/src/app/autotests/settingscontrollertest.cpp b/src/app/autotests/settingscontrollertest.cpp
index a175054..32c693e 100644
--- a/src/app/autotests/settingscontrollertest.cpp
+++ b/src/app/autotests/settingscontrollertest.cpp
@@ -6,6 +6,7 @@
#include <QCoreApplication>
#include <QSettings>
+#include <QSignalSpy>
#include <QStandardPaths>
#include <QTest>
@@ -21,6 +22,7 @@ private Q_SLOTS:
void initTestCase();
void init();
void usesRhythmDefault();
+ void persistsAutomaticTestQuestionAdvancement();
void routesTempoAndSubdivisionsByPlayMode();
void clampsRhythmTempo();
};
@@ -44,6 +46,32 @@ void SettingsControllerTest::usesRhythmDefault()
QCOMPARE(controller.rhythmTempo(), 45);
}
+void SettingsControllerTest::persistsAutomaticTestQuestionAdvancement()
+{
+ SettingsController controller;
+ QCOMPARE(controller.automaticallyAdvanceTestQuestions(), false);
+
+ QSignalSpy spy(&controller, &SettingsController::automaticallyAdvanceTestQuestionsChanged);
+ controller.setAutomaticallyAdvanceTestQuestions(true);
+ QCOMPARE(controller.automaticallyAdvanceTestQuestions(), true);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0).at(0).toBool(), true);
+
+ controller.setAutomaticallyAdvanceTestQuestions(true);
+ QCOMPARE(spy.count(), 1);
+
+ SettingsController reloadedController;
+ QCOMPARE(reloadedController.automaticallyAdvanceTestQuestions(), true);
+
+ controller.setAutomaticallyAdvanceTestQuestions(false);
+ QCOMPARE(controller.automaticallyAdvanceTestQuestions(), false);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.at(1).at(0).toBool(), false);
+
+ SettingsController resetController;
+ QCOMPARE(resetController.automaticallyAdvanceTestQuestions(), false);
+}
+
void SettingsControllerTest::routesTempoAndSubdivisionsByPlayMode()
{
SettingsController controller;
diff --git a/src/app/qml/exercises/ExerciseClappingView.qml b/src/app/qml/exercises/ExerciseClappingView.qml
index 219cb33..ee43413 100644
--- a/src/app/qml/exercises/ExerciseClappingView.qml
+++ b/src/app/qml/exercises/ExerciseClappingView.qml
@@ -157,7 +157,7 @@ ExerciseContent {
const finalScore = Core.exerciseSessionController.recordTestScore(internal.score, Core.settingsController.testExerciseCount);
if (finalScore >= 0) {
internal.score = finalScore;
- } else {
+ } else if (Core.settingsController.automaticallyAdvanceTestQuestions) {
testNextQuestionTimer.restart();
}
}
@@ -461,7 +461,7 @@ ExerciseContent {
Layout.preferredWidth: exerciseHeader.actionButtonWidth
enabled: internal.microphoneReady && internal.viewState !== "counting" && internal.viewState !== "listening" && internal.viewState !== "analyzing"
- text: internal.expectedOnsets.length === 0 || internal.viewState === "finished" ? i18n("New Question") : i18n("Start")
+ text: Core.exerciseSessionController.isTest && internal.viewState === "finished" ? i18n("Next Question") : internal.expectedOnsets.length === 0 || internal.viewState === "finished" ? i18n("New Question") : i18n("Start")
onClicked: {
if (internal.expectedOnsets.length === 0 || internal.viewState === "finished") {
diff --git a/src/app/qml/exercises/ExerciseSingingView.qml b/src/app/qml/exercises/ExerciseSingingView.qml
index ea2d3bf..6823fc2 100644
--- a/src/app/qml/exercises/ExerciseSingingView.qml
+++ b/src/app/qml/exercises/ExerciseSingingView.qml
@@ -190,7 +190,7 @@ ExerciseContent {
const finalScore = Core.exerciseSessionController.recordTestScore(internal.score, Core.settingsController.testExerciseCount);
if (finalScore >= 0) {
internal.score = finalScore;
- } else {
+ } else if (Core.settingsController.automaticallyAdvanceTestQuestions) {
testNextQuestionTimer.restart();
}
}
@@ -687,7 +687,7 @@ ExerciseContent {
Layout.preferredWidth: exerciseHeader.actionButtonWidth
enabled: internal.microphoneReady && internal.viewState !== "counting" && internal.viewState !== "listening" && internal.viewState !== "analyzing"
- text: internal.targetNotes.length === 0 || internal.viewState === "finished" ? i18n("New Question") : i18n("Start")
+ text: Core.exerciseSessionController.isTest && internal.viewState === "finished" ? i18n("Next Question") : internal.targetNotes.length === 0 || internal.viewState === "finished" ? i18n("New Question") : i18n("Start")
onClicked: {
if (internal.targetNotes.length === 0 || internal.viewState === "finished") {
diff --git a/src/app/qml/exercises/ExerciseView.qml b/src/app/qml/exercises/ExerciseView.qml
index faaf7ce..4dff992 100644
--- a/src/app/qml/exercises/ExerciseView.qml
+++ b/src/app/qml/exercises/ExerciseView.qml
@@ -27,7 +27,7 @@ ExerciseContent {
highlightRightAnswer();
} else {
exerciseView.state = "waitingForNewQuestion";
- if (Core.exerciseSessionController.isTest) {
+ if (Core.exerciseSessionController.isTest && Core.settingsController.automaticallyAdvanceTestQuestions) {
testFeedbackTimer.restart();
}
}
@@ -67,8 +67,8 @@ ExerciseContent {
return Core.exerciseSessionController.colorForAnswerIndex(index, internal.colors);
}
function finishSingleAnswerFeedback(): void {
- exerciseView.state = Core.exerciseSessionController.isTest ? "waitingForAnswer" : "waitingForNewQuestion";
- if (Core.exerciseSessionController.isTest) {
+ exerciseView.state = "waitingForNewQuestion";
+ if (Core.exerciseSessionController.isTest && Core.settingsController.automaticallyAdvanceTestQuestions) {
nextTestExercise();
if (Core.exerciseSessionController.currentExercise === internal.maximumExercises + 1)
Core.exerciseSessionController.resetTest();
@@ -121,6 +121,7 @@ ExerciseContent {
answerItem.opacity = 1;
}
}
+ Core.soundController.stop();
generateNewQuestion();
Core.soundController.play();
}
@@ -365,7 +366,7 @@ ExerciseContent {
Layout.preferredWidth: exerciseHeader.actionButtonWidth
enabled: !animation.running && !testFeedbackTimer.running && !internal.exercisePlaying
- text: exerciseView.state === "waitingForNewQuestion" ? i18n("New Question") : i18n("Play Question")
+ text: exerciseView.state === "waitingForNewQuestion" ? Core.exerciseSessionController.isTest ? i18n("Next Question") : i18n("New Question") : i18n("Play Question")
onClicked: {
if (exerciseView.state === "waitingForNewQuestion") {
diff --git a/src/app/qml/pages/SettingsPage.qml b/src/app/qml/pages/SettingsPage.qml
index e87371a..0643caf 100644
--- a/src/app/qml/pages/SettingsPage.qml
+++ b/src/app/qml/pages/SettingsPage.qml
@@ -145,7 +145,7 @@ FormCard.FormCardPage {
contentItem: SettingsSlider {
from: 5
- label: i18n("Number of exercises")
+ label: i18n("Number of exercises in tests")
to: 20
value: Core.settingsController.testExerciseCount
@@ -154,6 +154,12 @@ FormCard.FormCardPage {
}
}
}
+ FormCard.FormSwitchDelegate {
+ checked: Core.settingsController.automaticallyAdvanceTestQuestions
+ text: i18n("Automatically advance to the next test question")
+
+ onToggled: Core.settingsController.automaticallyAdvanceTestQuestions = checked
+ }
}
FormCard.FormHeader {
title: i18n("Sound")
diff --git a/src/app/settingscontroller.cpp b/src/app/settingscontroller.cpp
index fcaf63e..a079743 100644
--- a/src/app/settingscontroller.cpp
+++ b/src/app/settingscontroller.cpp
@@ -24,6 +24,7 @@ void SettingsController::load()
settings.beginGroup(u"Settings"_s);
m_rhythmPatternCount = std::clamp(settings.value(u"RhythmPatternCount"_s, m_rhythmPatternCount).toInt(), 4, 16);
m_testExerciseCount = std::clamp(settings.value(u"TestExerciseCount"_s, m_testExerciseCount).toInt(), 5, 20);
+ m_automaticallyAdvanceTestQuestions = settings.value(u"AutomaticallyAdvanceTestQuestions"_s, m_automaticallyAdvanceTestQuestions).toBool();
m_volume = std::clamp(settings.value(u"Volume"_s, m_volume).toInt(), 0, 200);
m_pitch = std::clamp(settings.value(u"Pitch"_s, m_pitch).toInt(), -12, 12);
m_tempo = std::clamp(settings.value(u"Tempo"_s, m_tempo).toInt(), 1, 255);
@@ -96,6 +97,11 @@ int SettingsController::testExerciseCount() const
return m_testExerciseCount;
}
+bool SettingsController::automaticallyAdvanceTestQuestions() const
+{
+ return m_automaticallyAdvanceTestQuestions;
+}
+
int SettingsController::volume() const
{
return m_volume;
@@ -334,6 +340,17 @@ void SettingsController::setTestExerciseCount(int testExerciseCount)
emit testExerciseCountChanged(m_testExerciseCount);
}
+void SettingsController::setAutomaticallyAdvanceTestQuestions(bool automaticallyAdvance)
+{
+ if (m_automaticallyAdvanceTestQuestions == automaticallyAdvance) {
+ return;
+ }
+
+ m_automaticallyAdvanceTestQuestions = automaticallyAdvance;
+ write(u"AutomaticallyAdvanceTestQuestions"_s, m_automaticallyAdvanceTestQuestions);
+ emit automaticallyAdvanceTestQuestionsChanged(m_automaticallyAdvanceTestQuestions);
+}
+
void SettingsController::setVolume(int volume)
{
volume = std::clamp(volume, 0, 200);
diff --git a/src/app/settingscontroller.h b/src/app/settingscontroller.h
index d297c74..7e2ad52 100644
--- a/src/app/settingscontroller.h
+++ b/src/app/settingscontroller.h
@@ -16,6 +16,8 @@ class SettingsController : public QObject
Q_OBJECT
Q_PROPERTY(int rhythmPatternCount READ rhythmPatternCount WRITE setRhythmPatternCount NOTIFY rhythmPatternCountChanged)
Q_PROPERTY(int testExerciseCount READ testExerciseCount WRITE setTestExerciseCount NOTIFY testExerciseCountChanged)
+ Q_PROPERTY(bool automaticallyAdvanceTestQuestions READ automaticallyAdvanceTestQuestions WRITE setAutomaticallyAdvanceTestQuestions NOTIFY
+ automaticallyAdvanceTestQuestionsChanged)
Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged)
Q_PROPERTY(int pitch READ pitch WRITE setPitch NOTIFY pitchChanged)
Q_PROPERTY(int tempo READ tempo WRITE setTempo NOTIFY tempoChanged)
@@ -62,6 +64,7 @@ public:
int rhythmPatternCount() const;
int testExerciseCount() const;
+ bool automaticallyAdvanceTestQuestions() const;
int volume() const;
int pitch() const;
int tempo() const;
@@ -100,6 +103,7 @@ public:
public Q_SLOTS:
void setRhythmPatternCount(int rhythmPatternCount);
void setTestExerciseCount(int testExerciseCount);
+ void setAutomaticallyAdvanceTestQuestions(bool automaticallyAdvance);
void setVolume(int volume);
void setPitch(int pitch);
void setTempo(int tempo);
@@ -133,6 +137,7 @@ public Q_SLOTS:
Q_SIGNALS:
void rhythmPatternCountChanged(int rhythmPatternCount);
void testExerciseCountChanged(int testExerciseCount);
+ void automaticallyAdvanceTestQuestionsChanged(bool automaticallyAdvance);
void volumeChanged(int volume);
void pitchChanged(int pitch);
void tempoChanged(int tempo);
@@ -191,6 +196,7 @@ private:
int m_rhythmPatternCount = 4;
int m_testExerciseCount = 10;
+ bool m_automaticallyAdvanceTestQuestions = false;
int m_volume = 100;
int m_pitch = 0;
int m_tempo = 60;