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 =
114 QString(
"QPushButton { color: white; background: rgba(255,255,255,40);"
115 " border: 1px solid rgba(255,255,255,80); border-radius: 4px;"
116 " padding: 3px 10px; font-size: %1px; }"
117 "QPushButton:hover { background: rgba(255,255,255,60); }"
118 "QPushButton:disabled { color: rgba(255,255,255,80); background: rgba(80,80,80,60); }")
120 m_closeButton->setStyleSheet(navStyle);
121 m_hintButton->setStyleSheet(navStyle);
122 m_prevButton->setStyleSheet(navStyle);
123 m_nextButton->setStyleSheet(
124 QString(
"QPushButton { color: white; background: rgba(60,150,60,180);"
125 " border: 1px solid rgba(255,255,255,80); border-radius: 4px;"
126 " padding: 3px 10px; font-size: %1px; }"
127 "QPushButton:hover { background: rgba(60,180,60,200); }"
128 "QPushButton:disabled { background: rgba(100,100,100,80);"
129 " color: rgba(255,255,255,100); }")
132 m_bgColor = QColor(245, 245, 245, 230);
133 m_textColor = QColor(20, 20, 20);
134 m_hintColor = QColor(110, 90, 0, 230);
135 m_counterColor = QColor(80, 80, 80, 200);
137 const QString navStyle =
138 QString(
"QPushButton { color: rgb(20,20,20); background: rgba(0,0,0,20);"
139 " border: 1px solid rgba(0,0,0,80); border-radius: 4px;"
140 " padding: 3px 10px; font-size: %1px; }"
141 "QPushButton:hover { background: rgba(0,0,0,40); }"
142 "QPushButton:disabled { color: rgba(0,0,0,60); background: rgba(0,0,0,10); }")
144 m_closeButton->setStyleSheet(navStyle);
145 m_hintButton->setStyleSheet(navStyle);
146 m_prevButton->setStyleSheet(navStyle);
147 m_nextButton->setStyleSheet(
148 QString(
"QPushButton { color: white; background: rgba(40,130,40,220);"
149 " border: 1px solid rgba(0,100,0,150); border-radius: 4px;"
150 " padding: 3px 10px; font-size: %1px; }"
151 "QPushButton:hover { background: rgba(40,160,40,230); }"
152 "QPushButton:disabled { background: rgba(150,150,150,80);"
153 " color: rgba(255,255,255,120); }")
157 m_stepCounter->setStyleSheet(
158 QString(
"color: rgba(%1,%2,%3,%4); font-size: %5px;")
159 .arg(m_counterColor.red()).arg(m_counterColor.green())
160 .arg(m_counterColor.blue()).arg(m_counterColor.alpha()).arg(scaledFontPx(12)));
161 m_instructionLabel->setStyleSheet(
162 QString(
"color: rgb(%1,%2,%3); font-size: %4px; font-weight: bold;")
163 .arg(m_textColor.red()).arg(m_textColor.green()).arg(m_textColor.blue())
164 .arg(scaledFontPx(14)));
165 m_hintLabel->setStyleSheet(
166 QString(
"color: rgba(%1,%2,%3,%4); font-size: %5px;")
167 .arg(m_hintColor.red()).arg(m_hintColor.green())
168 .arg(m_hintColor.blue()).arg(m_hintColor.alpha()).arg(scaledFontPx(13)));
175 if (!parentWidget()) {
179 const int x = (parentWidget()->width() - width()) / 2;
180 const int y = parentWidget()->height() - height() - 12;
186 m_stepCounter->setText(tr(
"Step %1 of %2").arg(step + 1).arg(total));
188 m_hintLabel->setText(stepData.
hint);
191 m_hintVisible =
false;
193 m_hintButton->setText(tr(
"Hint"));
196 const bool isLastStep = (step == total - 1);
198 updateNextButton(isLastStep);
200 m_nextButton->setEnabled(isObserveStep);
202 m_prevButton->setEnabled(step > 0);
210 const ExerciseStep &stepData = m_engine->currentStepData();
211 const int step = m_engine->currentStep();
212 const int total = m_engine->totalSteps();
214 m_stepCounter->setText(tr(
"Step %1 of %2").arg(step + 1).arg(total));
216 m_hintLabel->setText(stepData.
hint);
217 updateNextButton(step == total - 1);
219 m_closeButton->setText(tr(
"Exit"));
220 m_closeButton->setToolTip(tr(
"Close exercise"));
221 m_hintButton->setText(m_hintVisible ? tr(
"Hide hint") : tr(
"Hint"));
222 m_prevButton->setText(tr(
"← Back"));
230 m_stepCounter->setText({});
231 m_instructionLabel->setText(tr(
"Exercise complete! Well done."));
233 m_hintButton->hide();
234 m_prevButton->hide();
235 m_nextButton->setText(tr(
"Close"));
236 m_nextButton->setEnabled(
true);
237 disconnect(m_nextButton, &QPushButton::clicked,
nullptr,
nullptr);
243void ExerciseOverlay::updateNextButton(
bool isLastStep)
245 m_nextButton->setText(isLastStep ? tr(
"Finish") : tr(
"Next →"));
251 QPainter painter(
this);
252 painter.setRenderHint(QPainter::Antialiasing);
253 painter.setBrush(m_bgColor);
254 painter.setPen(Qt::NoPen);
255 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