22 setAttribute(Qt::WA_TranslucentBackground);
27 this, &ExerciseOverlay::applyTheme);
30void ExerciseOverlay::setupUi()
32 auto *outerLayout =
new QVBoxLayout(
this);
33 outerLayout->setContentsMargins(12, 12, 12, 12);
34 outerLayout->setSpacing(8);
37 auto *topRow =
new QHBoxLayout;
38 m_stepCounter =
new QLabel(
this);
39 topRow->addWidget(m_stepCounter);
41 outerLayout->addLayout(topRow);
44 m_instructionLabel =
new QLabel(
this);
45 m_instructionLabel->setWordWrap(
true);
46 outerLayout->addWidget(m_instructionLabel);
49 m_hintLabel =
new QLabel(
this);
50 m_hintLabel->setWordWrap(
true);
52 outerLayout->addWidget(m_hintLabel);
55 auto *btnRow =
new QHBoxLayout;
56 btnRow->setSpacing(6);
58 m_closeButton =
new QPushButton(tr(
"Exit"),
this);
59 m_closeButton->setToolTip(tr(
"Close exercise"));
61 m_hintButton =
new QPushButton(tr(
"Hint"),
this);
63 m_prevButton =
new QPushButton(tr(
"← Back"),
this);
65 m_nextButton =
new QPushButton(tr(
"Next →"),
this);
66 m_nextButton->setEnabled(
false);
68 btnRow->addWidget(m_closeButton);
69 btnRow->addWidget(m_hintButton);
71 btnRow->addWidget(m_prevButton);
72 btnRow->addWidget(m_nextButton);
73 outerLayout->addLayout(btnRow);
78 connect(m_hintButton, &QPushButton::clicked,
this, [
this] {
79 m_hintVisible = !m_hintVisible;
80 m_hintLabel->setVisible(m_hintVisible);
81 m_hintButton->setText(m_hintVisible ? tr(
"Hide hint") : tr(
"Hint"));
85 connect(m_prevButton, &QPushButton::clicked,
this, [
this] {
86 m_engine->goToPreviousStep();
88 connect(m_nextButton, &QPushButton::clicked,
this, [
this] {
89 m_engine->advanceStep();
95int ExerciseOverlay::scaledFontPx(
int basePx)
97 constexpr int baselinePx = 13;
98 const int currentPx = QFontInfo(QApplication::font()).pixelSize();
99 return qRound(
static_cast<double>(basePx) * currentPx / baselinePx);
102void ExerciseOverlay::applyTheme()
105 const int navFontPx = scaledFontPx(13);
108 m_bgColor = QColor(30, 30, 30, 200);
109 m_textColor = QColor(255, 255, 255);
110 m_hintColor = QColor(255, 255, 180, 230);
111 m_counterColor = QColor(255, 255, 255, 200);
113 const QString navStyle = QString(
"QPushButton { color: white; background: rgba(255,255,255,40); border: 1px solid rgba(255,255,255,80); border-radius: 4px; padding: 3px 10px; font-size: %1px; } QPushButton:hover { background: rgba(255,255,255,60); } QPushButton:disabled { color: rgba(255,255,255,80); background: rgba(80,80,80,60); }").arg(navFontPx);
114 m_closeButton->setStyleSheet(navStyle);
115 m_hintButton->setStyleSheet(navStyle);
116 m_prevButton->setStyleSheet(navStyle);
117 m_nextButton->setStyleSheet(QString(
"QPushButton { color: white; background: rgba(60,150,60,180); border: 1px solid rgba(255,255,255,80); border-radius: 4px; padding: 3px 10px; font-size: %1px; } QPushButton:hover { background: rgba(60,180,60,200); } QPushButton:disabled { background: rgba(100,100,100,80); color: rgba(255,255,255,100); }").arg(navFontPx));
119 m_bgColor = QColor(245, 245, 245, 230);
120 m_textColor = QColor(20, 20, 20);
121 m_hintColor = QColor(110, 90, 0, 230);
122 m_counterColor = QColor(80, 80, 80, 200);
124 const QString navStyle = QString(
"QPushButton { color: rgb(20,20,20); background: rgba(0,0,0,20); border: 1px solid rgba(0,0,0,80); border-radius: 4px; padding: 3px 10px; font-size: %1px; } QPushButton:hover { background: rgba(0,0,0,40); } QPushButton:disabled { color: rgba(0,0,0,60); background: rgba(0,0,0,10); }").arg(navFontPx);
125 m_closeButton->setStyleSheet(navStyle);
126 m_hintButton->setStyleSheet(navStyle);
127 m_prevButton->setStyleSheet(navStyle);
128 m_nextButton->setStyleSheet(QString(
"QPushButton { color: white; background: rgba(40,130,40,220); border: 1px solid rgba(0,100,0,150); border-radius: 4px; padding: 3px 10px; font-size: %1px; } QPushButton:hover { background: rgba(40,160,40,230); } QPushButton:disabled { background: rgba(150,150,150,80); color: rgba(255,255,255,120); }").arg(navFontPx));
131 m_stepCounter->setStyleSheet(
132 QString(
"color: rgba(%1,%2,%3,%4); font-size: %5px;")
133 .arg(m_counterColor.red()).arg(m_counterColor.green())
134 .arg(m_counterColor.blue()).arg(m_counterColor.alpha()).arg(scaledFontPx(12)));
135 m_instructionLabel->setStyleSheet(
136 QString(
"color: rgb(%1,%2,%3); font-size: %4px; font-weight: bold;")
137 .arg(m_textColor.red()).arg(m_textColor.green()).arg(m_textColor.blue())
138 .arg(scaledFontPx(14)));
139 m_hintLabel->setStyleSheet(
140 QString(
"color: rgba(%1,%2,%3,%4); font-size: %5px;")
141 .arg(m_hintColor.red()).arg(m_hintColor.green())
142 .arg(m_hintColor.blue()).arg(m_hintColor.alpha()).arg(scaledFontPx(13)));
149 if (!parentWidget()) {
153 const int x = (parentWidget()->width() - width()) / 2;
154 const int y = parentWidget()->height() - height() - 12;
160 m_stepCounter->setText(tr(
"Step %1 of %2").arg(step + 1).arg(total));
162 m_hintLabel->setText(stepData.
hint);
165 m_hintVisible =
false;
167 m_hintButton->setText(tr(
"Hint"));
170 const bool isLastStep = (step == total - 1);
172 updateNextButton(isLastStep);
174 m_nextButton->setEnabled(isObserveStep);
176 m_prevButton->setEnabled(step > 0);
184 const ExerciseStep &stepData = m_engine->currentStepData();
185 const int step = m_engine->currentStep();
186 const int total = m_engine->totalSteps();
188 m_stepCounter->setText(tr(
"Step %1 of %2").arg(step + 1).arg(total));
190 m_hintLabel->setText(stepData.
hint);
191 updateNextButton(step == total - 1);
193 m_closeButton->setText(tr(
"Exit"));
194 m_closeButton->setToolTip(tr(
"Close exercise"));
195 m_hintButton->setText(m_hintVisible ? tr(
"Hide hint") : tr(
"Hint"));
196 m_prevButton->setText(tr(
"← Back"));
204 m_stepCounter->setText({});
205 m_instructionLabel->setText(tr(
"Exercise complete! Well done."));
207 m_hintButton->hide();
208 m_prevButton->hide();
209 m_nextButton->setText(tr(
"Close"));
210 m_nextButton->setEnabled(
true);
211 disconnect(m_nextButton, &QPushButton::clicked,
nullptr,
nullptr);
217void ExerciseOverlay::updateNextButton(
bool isLastStep)
219 m_nextButton->setText(isLastStep ? tr(
"Finish") : tr(
"Next →"));
225 QPainter painter(
this);
226 painter.setRenderHint(QPainter::Antialiasing);
227 painter.setBrush(m_bgColor);
228 painter.setPen(Qt::NoPen);
229 painter.drawRoundedRect(rect(), 8, 8);
Theme management types and singleton ThemeManager.
void onExerciseCompleted()
ExerciseOverlay(ExerciseEngine *engine, QWidget *parent=nullptr)
void repositionToParent()
void onStepChanged(int step, int total, const ExerciseStep &stepData)
void paintEvent(QPaintEvent *event) override
static ThemeManager & instance()
Returns the singleton ThemeManager instance.
void themeChanged()
Emitted whenever the active theme changes.
static Theme effectiveTheme()
Returns the effective theme (Light or Dark), resolving System to the OS preference.
QVector< ExerciseConnectionRequirement > requiredConnections
QVector< ExerciseElementRequirement > requiredElements