mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-01-17 15:32:11 -08:00
Improve with sequencing and better rendering.
Took 3 minutes Took 18 seconds
This commit is contained in:
@@ -1,70 +1,168 @@
|
||||
#include "tutorial_controller.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
TutorialController::TutorialController(QWidget *_tutorializedWidget)
|
||||
: QObject(_tutorializedWidget), tutorializedWidget(_tutorializedWidget)
|
||||
{
|
||||
tutorialOverlay = new TutorialOverlay(nullptr); // nullptr parent because we overlay this on top of *everything*
|
||||
|
||||
// Stay above everything
|
||||
tutorialOverlay->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
tutorialOverlay = new TutorialOverlay(tutorializedWidget->window());
|
||||
|
||||
// Make it frameless + translucent
|
||||
tutorialOverlay->setWindowFlags(tutorialOverlay->windowFlags() | Qt::FramelessWindowHint);
|
||||
tutorialOverlay->setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
// Hide until started
|
||||
// hide until start
|
||||
tutorialOverlay->hide();
|
||||
|
||||
connect(tutorialOverlay, &TutorialOverlay::nextStep, this, &TutorialController::progressTutorial);
|
||||
|
||||
connect(tutorialOverlay, &TutorialOverlay::nextStep, this, &TutorialController::nextStep);
|
||||
connect(tutorialOverlay, &TutorialOverlay::skipTutorial, this, [this]() { tutorialOverlay->hide(); });
|
||||
}
|
||||
|
||||
void TutorialController::addStep(const TutorialStep &step)
|
||||
void TutorialController::addSequence(const TutorialSequence &seq)
|
||||
{
|
||||
steps.append(step);
|
||||
sequences.append(seq);
|
||||
}
|
||||
|
||||
void TutorialController::start()
|
||||
{
|
||||
if (steps.isEmpty()) {
|
||||
if (sequences.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Align ourselves and show
|
||||
QPoint topLeft = tutorializedWidget->mapToGlobal(QPoint(0, 0));
|
||||
QSize windowSize = tutorializedWidget->size();
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
QWidget *win = tutorializedWidget->window();
|
||||
tutorialOverlay->parentResized();
|
||||
tutorialOverlay->setGeometry(QRect(QPoint(0, 0), win->size()));
|
||||
tutorialOverlay->show();
|
||||
tutorialOverlay->raise();
|
||||
tutorialOverlay->parentResized();
|
||||
|
||||
tutorialOverlay->setGeometry(QRect(topLeft, windowSize));
|
||||
tutorialOverlay->show();
|
||||
tutorialOverlay->raise();
|
||||
currentSequence = 0;
|
||||
currentStep = 0;
|
||||
showStep();
|
||||
});
|
||||
}
|
||||
|
||||
void TutorialController::nextStep()
|
||||
{
|
||||
// advance within sequence
|
||||
currentStep++;
|
||||
|
||||
if (currentSequence < 0) {
|
||||
return; // defensive in case we haven't started yet
|
||||
}
|
||||
|
||||
if (currentStep >= sequences[currentSequence].steps.size()) {
|
||||
// advance to next sequence
|
||||
nextSequence();
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the tutorial
|
||||
currentStep = 0;
|
||||
showStep();
|
||||
}
|
||||
|
||||
void TutorialController::progressTutorial()
|
||||
void TutorialController::prevStep()
|
||||
{
|
||||
currentStep++;
|
||||
if (currentStep >= steps.size()) {
|
||||
tutorialOverlay->hide();
|
||||
if (currentSequence < 0) {
|
||||
return; // defensive in case we haven't started yet
|
||||
}
|
||||
|
||||
if (currentStep == 0) {
|
||||
prevSequence();
|
||||
return;
|
||||
}
|
||||
|
||||
currentStep--;
|
||||
showStep();
|
||||
}
|
||||
|
||||
void TutorialController::nextSequence()
|
||||
{
|
||||
if (currentSequence < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// run exit for the last step of the current sequence (showStep handles previous onExit,
|
||||
// but ensure we run it here because we're jumping sequence)
|
||||
// We'll increment sequence and then call showStep which will call the onEnter for the new step.
|
||||
currentSequence++;
|
||||
currentStep = 0;
|
||||
|
||||
if (currentSequence >= sequences.size()) {
|
||||
exitTutorial();
|
||||
return;
|
||||
}
|
||||
|
||||
showStep();
|
||||
}
|
||||
|
||||
void TutorialController::prevSequence()
|
||||
{
|
||||
if (currentSequence <= 0) {
|
||||
// already at first sequence -> stay
|
||||
return;
|
||||
}
|
||||
|
||||
// go to last step of previous sequence
|
||||
currentSequence--;
|
||||
currentStep = sequences[currentSequence].steps.size() - 1;
|
||||
showStep();
|
||||
}
|
||||
|
||||
void TutorialController::exitTutorial()
|
||||
{
|
||||
// Run onExit for the current step if present
|
||||
if (currentSequence >= 0 && currentStep >= 0 && currentSequence < sequences.size() &&
|
||||
currentStep < sequences[currentSequence].steps.size()) {
|
||||
const auto &curStep = sequences[currentSequence].steps[currentStep];
|
||||
if (curStep.onExit) {
|
||||
curStep.onExit();
|
||||
}
|
||||
}
|
||||
|
||||
tutorialOverlay->hide();
|
||||
|
||||
// reset indices so start() can be called again cleanly
|
||||
currentSequence = -1;
|
||||
currentStep = -1;
|
||||
}
|
||||
|
||||
void TutorialController::showStep()
|
||||
{
|
||||
const auto &step = steps[currentStep];
|
||||
// bounds checks
|
||||
if (currentSequence < 0 || currentSequence >= sequences.size()) {
|
||||
return;
|
||||
}
|
||||
const auto &seq = sequences[currentSequence];
|
||||
if (currentStep < 0 || currentStep >= seq.steps.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Run any action associated with previous steps
|
||||
if (currentStep < 0) {
|
||||
const auto &previousStep = steps[currentStep - 1];
|
||||
// run onExit for the previous step (including if previous step was in previous sequence)
|
||||
if (!(currentSequence == 0 && currentStep == 0)) {
|
||||
int prevSeq = currentSequence;
|
||||
int prevStepIndex = currentStep - 1;
|
||||
if (prevStepIndex < 0) {
|
||||
// previous is last step of previous sequence
|
||||
prevSeq = currentSequence - 1;
|
||||
if (prevSeq >= 0) {
|
||||
prevStepIndex = sequences[prevSeq].steps.size() - 1;
|
||||
} else {
|
||||
prevStepIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (previousStep.onExit) {
|
||||
previousStep.onExit();
|
||||
if (prevSeq >= 0 && prevStepIndex >= 0) {
|
||||
const auto &previousStep = sequences[prevSeq].steps[prevStepIndex];
|
||||
if (previousStep.onExit) {
|
||||
previousStep.onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// current step
|
||||
const auto &step = seq.steps[currentStep];
|
||||
|
||||
// Run any action associated with this step
|
||||
if (step.onEnter) {
|
||||
step.onEnter();
|
||||
|
||||
@@ -11,8 +11,19 @@ struct TutorialStep
|
||||
{
|
||||
QWidget *targetWidget;
|
||||
QString text;
|
||||
std::function<void()> onEnter; // Optional function to run when this step starts
|
||||
std::function<void()> onExit; // Optional function to run when step ends
|
||||
std::function<void()> onEnter = nullptr; // Optional function to run when this step starts
|
||||
std::function<void()> onExit = nullptr; // Optional function to run when step ends
|
||||
};
|
||||
|
||||
struct TutorialSequence
|
||||
{
|
||||
QString name;
|
||||
QVector<TutorialStep> steps;
|
||||
|
||||
void addStep(const TutorialStep &step)
|
||||
{
|
||||
steps.append(step);
|
||||
}
|
||||
};
|
||||
|
||||
class TutorialController : public QObject
|
||||
@@ -20,17 +31,22 @@ class TutorialController : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void progressTutorial();
|
||||
void start();
|
||||
void nextStep();
|
||||
void prevStep();
|
||||
void nextSequence();
|
||||
void prevSequence();
|
||||
void exitTutorial();
|
||||
|
||||
public:
|
||||
explicit TutorialController(QWidget *_tutorializedWidget);
|
||||
|
||||
void addStep(const TutorialStep &step);
|
||||
void start();
|
||||
void addSequence(const TutorialSequence &step);
|
||||
|
||||
private:
|
||||
QWidget *tutorializedWidget;
|
||||
QVector<TutorialStep> steps;
|
||||
QVector<TutorialSequence> sequences;
|
||||
int currentSequence = -1;
|
||||
int currentStep = -1;
|
||||
|
||||
TutorialOverlay *tutorialOverlay;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent)
|
||||
TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent, Qt::Window)
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
@@ -16,7 +16,13 @@ TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent)
|
||||
|
||||
// This ensures the overlay stays exactly over the parent
|
||||
if (parent) {
|
||||
setGeometry(parent->rect());
|
||||
QRect r = parent->rect();
|
||||
|
||||
// convert the parent’s rect to screen coordinates
|
||||
QPoint globalTopLeft = parent->mapToGlobal(QPoint(0, 0));
|
||||
r.moveTopLeft(globalTopLeft);
|
||||
|
||||
setGeometry(r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +105,10 @@ QRect TutorialOverlay::computeBubbleRect(const QRect &hole) const
|
||||
void TutorialOverlay::parentResized()
|
||||
{
|
||||
if (parentWidget()) {
|
||||
setGeometry(parentWidget()->rect());
|
||||
QRect r = parentWidget()->rect();
|
||||
QPoint globalTopLeft = parentWidget()->mapToGlobal(QPoint(0, 0));
|
||||
r.moveTopLeft(globalTopLeft);
|
||||
setGeometry(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user