Compare commits

...

5 Commits

Author SHA1 Message Date
Lukas Brübach
54ee406a58 tutorialize deck editors
Took 8 seconds

Took 12 minutes
2025-12-07 23:12:03 +01:00
Lukas Brübach
f6a1f34864 tutorialize home
Took 14 seconds


Took 2 minutes
2025-12-07 23:00:09 +01:00
Lukas Brübach
06a162c1f3 Bubbling.
Took 16 seconds

# Commit time for manual adjustment:
# Took 8 seconds

Took 16 seconds

# Commit time for manual adjustment:
# Took 6 seconds

Took 9 seconds

# Commit time for manual adjustment:
# Took 8 seconds


Took 14 seconds
2025-12-07 22:58:05 +01:00
Lukas Brübach
d074fd5491 Improve with sequencing and better rendering.
Took 3 minutes


Took 18 seconds
2025-12-07 22:03:25 +01:00
Brübach, Lukas
319e8fe7c9 [App] First-run tutorial 2025-12-06 22:19:59 +01:00
13 changed files with 642 additions and 8 deletions

View File

@@ -168,6 +168,9 @@ set(cockatrice_SOURCES
src/interface/widgets/general/layout_containers/flow_widget.cpp
src/interface/widgets/general/layout_containers/overlap_control_widget.cpp
src/interface/widgets/general/layout_containers/overlap_widget.cpp
src/interface/widgets/general/tutorial/tutorial_bubble_widget.cpp
src/interface/widgets/general/tutorial/tutorial_controller.cpp
src/interface/widgets/general/tutorial/tutorial_overlay.cpp
src/interface/widgets/menus/deck_editor_menu.cpp
src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
src/interface/widgets/printing_selector/card_amount_widget.cpp

View File

@@ -5,6 +5,7 @@
#include "../../window_main.h"
#include "background_sources.h"
#include "home_styled_button.h"
#include "tutorial/tutorial_controller.h"
#include <QGroupBox>
#include <QPainter>
@@ -48,6 +49,30 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
&HomeWidget::initializeBackgroundFromSource);
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundShuffleFrequencyChanged, this,
&HomeWidget::onBackgroundShuffleFrequencyChanged);
tutorialController = new TutorialController(this);
auto sequence = TutorialSequence();
sequence.addStep({connectButton, "Connect to a server to play here!"});
sequence.addStep({visualDeckEditorButton, "Create a new deck from cards in the database here!"});
sequence.addStep({visualDeckStorageButton, "Browse the decks in your local collection."});
sequence.addStep({visualDatabaseDisplayButton, "View the card database here."});
sequence.addStep(
{edhrecButton, "Browse EDHRec, an external service designed to provide card recommendations for decks."});
sequence.addStep({archidektButton, "Browse Archidekt, an external service that allows users to store "
"decklists and import them to your local collection."});
sequence.addStep({replaybutton, "View replays of your past games here."});
sequence.addStep({exitButton, "Exit the application."});
tutorialController->addSequence(sequence);
}
void HomeWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
if (!tutorialStarted) {
tutorialStarted = true;
// Start on next event loop iteration so everything is fully painted
QTimer::singleShot(3, tutorialController, [this] { tutorialController->start(); });
}
}
void HomeWidget::initializeBackgroundFromSource()
@@ -181,29 +206,29 @@ QGroupBox *HomeWidget::createButtons()
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
boxLayout->addWidget(connectButton);
auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->openDeckInNewTab(nullptr); });
boxLayout->addWidget(visualDeckEditorButton);
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
boxLayout->addWidget(visualDeckStorageButton);
auto visualDatabaseDisplayButton = new HomeStyledButton(tr("Browse Card Database"), gradientColors);
visualDatabaseDisplayButton = new HomeStyledButton(tr("Browse Card Database"), gradientColors);
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
&TabSupervisor::addVisualDatabaseDisplayTab);
boxLayout->addWidget(visualDatabaseDisplayButton);
auto edhrecButton = new HomeStyledButton(tr("Browse EDHRec"), gradientColors);
edhrecButton = new HomeStyledButton(tr("Browse EDHRec"), gradientColors);
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
boxLayout->addWidget(edhrecButton);
auto archidektButton = new HomeStyledButton(tr("Browse Archidekt"), gradientColors);
archidektButton = new HomeStyledButton(tr("Browse Archidekt"), gradientColors);
connect(archidektButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addArchidektTab);
boxLayout->addWidget(archidektButton);
auto replaybutton = new HomeStyledButton(tr("View Replays"), gradientColors);
replaybutton = new HomeStyledButton(tr("View Replays"), gradientColors);
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
boxLayout->addWidget(replaybutton);
if (qobject_cast<MainWindow *>(tabSupervisor->parentWidget())) {
auto exitButton = new HomeStyledButton(tr("Quit"), gradientColors);
exitButton = new HomeStyledButton(tr("Quit"), gradientColors);
connect(exitButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
&MainWindow::actExit);
boxLayout->addWidget(exitButton);

View File

@@ -24,9 +24,18 @@ public:
HomeWidget(QWidget *parent, TabSupervisor *tabSupervisor);
void updateRandomCard();
QPair<QColor, QColor> extractDominantColors(const QPixmap &pixmap);
HomeStyledButton *connectButton;
HomeStyledButton *visualDeckEditorButton;
HomeStyledButton *visualDeckStorageButton;
HomeStyledButton *visualDatabaseDisplayButton;
HomeStyledButton *edhrecButton;
HomeStyledButton *archidektButton;
HomeStyledButton *replaybutton;
HomeStyledButton *exitButton;
public slots:
void paintEvent(QPaintEvent *event) override;
void showEvent(QShowEvent *event) override;
void initializeBackgroundFromSource();
void onBackgroundShuffleFrequencyChanged();
void updateBackgroundProperties();
@@ -39,11 +48,12 @@ private:
QTimer *cardChangeTimer;
TabSupervisor *tabSupervisor;
QPixmap background;
TutorialController *tutorialController;
bool tutorialStarted = false;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
DeckLoader *backgroundSourceDeck;
QPixmap overlay;
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
};
#endif // HOME_WIDGET_H

View File

@@ -0,0 +1,56 @@
#include "tutorial_bubble_widget.h"
BubbleWidget::BubbleWidget(QWidget *parent) : QFrame(parent)
{
setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
setStyleSheet("background:white; border-radius:8px;");
QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(12, 10, 12, 10);
layout->setHorizontalSpacing(8);
layout->setVerticalSpacing(8);
counterLabel = new QLabel(this);
counterLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
closeButton = new QPushButton("", this);
closeButton->setFixedSize(20, 20);
textLabel = new QLabel(this);
textLabel->setWordWrap(true);
textLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
textLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
textLabel->setStyleSheet("color:black;"); // guard against global styles
// nav buttons
previousSequenceButton = new QPushButton("<<", this);
previousStepButton = new QPushButton("<", this);
nextStepButton = new QPushButton(">", this);
nextSequenceButton = new QPushButton(">>", this);
QHBoxLayout *navLayout = new QHBoxLayout;
navLayout->addStretch();
navLayout->addWidget(previousSequenceButton);
navLayout->addWidget(previousStepButton);
navLayout->addWidget(nextStepButton);
navLayout->addWidget(nextSequenceButton);
// Layout
layout->addWidget(counterLabel, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
layout->addItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 1);
layout->addWidget(closeButton, 0, 2, Qt::AlignRight);
layout->addWidget(textLabel, 1, 0, 1, 3);
layout->addLayout(navLayout, 2, 0, 1, 3);
// Make column 1 take extra space so text gets room to expand/wrap
layout->setColumnStretch(1, 1);
// sensible default maximum width for bubble so text will wrap
setMaximumWidth(420);
}
void BubbleWidget::setText(const QString &text)
{
textLabel->setText(text);
update();
}

View File

@@ -0,0 +1,24 @@
#ifndef COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H
#define COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H
#include <QFrame>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
class BubbleWidget : public QFrame
{
Q_OBJECT
public:
QLabel *textLabel;
QLabel *counterLabel;
QPushButton *closeButton;
QPushButton *previousSequenceButton;
QPushButton *previousStepButton;
QPushButton *nextStepButton;
QPushButton *nextSequenceButton;
BubbleWidget(QWidget *parent);
void setText(const QString &text);
};
#endif // COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H

View File

@@ -0,0 +1,181 @@
#include "tutorial_controller.h"
#include <QTimer>
TutorialController::TutorialController(QWidget *_tutorializedWidget)
: QObject(_tutorializedWidget), tutorializedWidget(_tutorializedWidget)
{
tutorialOverlay = new TutorialOverlay(tutorializedWidget->window());
// Make it frameless + translucent
tutorialOverlay->setWindowFlags(tutorialOverlay->windowFlags() | Qt::FramelessWindowHint);
tutorialOverlay->setAttribute(Qt::WA_TranslucentBackground);
// hide until start
tutorialOverlay->hide();
connect(tutorialOverlay, &TutorialOverlay::nextStep, this, &TutorialController::nextStep);
connect(tutorialOverlay, &TutorialOverlay::prevStep, this, &TutorialController::prevStep);
connect(tutorialOverlay, &TutorialOverlay::nextSequence, this, &TutorialController::nextSequence);
connect(tutorialOverlay, &TutorialOverlay::prevSequence, this, &TutorialController::prevSequence);
connect(tutorialOverlay, &TutorialOverlay::skipTutorial, this, &TutorialController::exitTutorial);
}
void TutorialController::addSequence(const TutorialSequence &seq)
{
sequences.append(seq);
}
void TutorialController::start()
{
if (sequences.isEmpty()) {
return;
}
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();
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;
}
showStep();
}
void TutorialController::prevStep()
{
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
currentStep = 0;
showStep();
return;
}
currentSequence--;
currentStep = 0;
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()
{
// bounds checks
if (currentSequence < 0 || currentSequence >= sequences.size()) {
return;
}
const auto &seq = sequences[currentSequence];
if (currentStep < 0 || currentStep >= seq.steps.size()) {
return;
}
// 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 (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();
}
tutorialOverlay->setTargetWidget(step.targetWidget);
tutorialOverlay->setText(step.text);
tutorialOverlay->parentResized();
tutorialOverlay->raise();
tutorialOverlay->update();
}

View File

@@ -0,0 +1,57 @@
#ifndef COCKATRICE_TUTORIAL_CONTROLLER_H
#define COCKATRICE_TUTORIAL_CONTROLLER_H
#include "tutorial_overlay.h"
#include <QObject>
#include <QVector>
#include <functional>
struct TutorialStep
{
QWidget *targetWidget;
QString text;
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
{
Q_OBJECT
public slots:
void start();
void nextStep();
void prevStep();
void nextSequence();
void prevSequence();
void exitTutorial();
public:
explicit TutorialController(QWidget *_tutorializedWidget);
void addSequence(const TutorialSequence &step);
private:
QWidget *tutorializedWidget;
QVector<TutorialSequence> sequences;
int currentSequence = -1;
int currentStep = -1;
TutorialOverlay *tutorialOverlay;
void showStep();
};
#endif // COCKATRICE_TUTORIAL_CONTROLLER_H

View File

@@ -0,0 +1,178 @@
#include "tutorial_overlay.h"
#include <QLabel>
#include <QPaintEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPushButton>
#include <QVBoxLayout>
TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent, Qt::Window)
{
setAttribute(Qt::WA_TransparentForMouseEvents, false);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
// This ensures the overlay stays exactly over the parent
if (parent) {
QRect r = parent->rect();
// convert the parents rect to screen coordinates
QPoint globalTopLeft = parent->mapToGlobal(QPoint(0, 0));
r.moveTopLeft(globalTopLeft);
setGeometry(r);
}
bubble = new BubbleWidget(this);
bubble->hide();
connect(bubble->nextStepButton, &QPushButton::clicked, this, &TutorialOverlay::nextStep);
connect(bubble->previousStepButton, &QPushButton::clicked, this, &TutorialOverlay::prevStep);
connect(bubble->previousSequenceButton, &QPushButton::clicked, this, &TutorialOverlay::prevSequence);
connect(bubble->nextSequenceButton, &QPushButton::clicked, this, &TutorialOverlay::nextSequence);
connect(bubble->closeButton, &QPushButton::clicked, this, &TutorialOverlay::skipTutorial);
}
void TutorialOverlay::setTargetWidget(QWidget *w)
{
targetWidget = w;
update();
}
void TutorialOverlay::setText(const QString &t)
{
tutorialText = t;
bubble->setText(tutorialText);
bubble->adjustSize(); // let layout recalc sizes
QSize bsize = bubble->sizeHint();
const QSize minSize(160, 60);
if (bsize.width() < minSize.width()) {
bsize.setWidth(minSize.width());
}
if (bsize.height() < minSize.height()) {
bsize.setHeight(minSize.height());
}
// Compute the bubble rect from the current target hole
QRect hole = targetRectOnOverlay().adjusted(-6, -6, 6, 6);
highlightBubbleRect = computeBubbleRect(hole, bsize);
bubble->setGeometry(highlightBubbleRect);
bubble->raise();
bubble->show();
update();
}
void TutorialOverlay::showEvent(QShowEvent *)
{
raise();
}
void TutorialOverlay::resizeEvent(QResizeEvent *)
{
update();
}
QRect TutorialOverlay::targetRectOnOverlay() const
{
if (!targetWidget) {
return QRect();
}
// Widget -> global screen coordinates
QPoint globalTopLeft = targetWidget->mapToGlobal(QPoint(0, 0));
// Global -> overlay-local coordinates
QPoint localTopLeft = mapFromGlobal(globalTopLeft);
return QRect(localTopLeft, targetWidget->size());
}
QRect TutorialOverlay::computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const
{
const int margin = 16;
QRect r = rect(); // overlay bounds
QRect bubble;
// Try right
bubble = QRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
if (r.contains(bubble)) {
return bubble;
}
// Try left
bubble = QRect(hole.left() - margin - bubbleSize.width(), hole.top(), bubbleSize.width(), bubbleSize.height());
if (r.contains(bubble)) {
return bubble;
}
// Try above, centered
bubble = QRect(hole.center().x() - bubbleSize.width() / 2, hole.top() - margin - bubbleSize.height(),
bubbleSize.width(), bubbleSize.height());
if (r.contains(bubble)) {
return bubble;
}
// Try below, centered
bubble = QRect(hole.center().x() - bubbleSize.width() / 2, hole.bottom() + margin, bubbleSize.width(),
bubbleSize.height());
if (r.contains(bubble)) {
return bubble;
}
// Last-resort: clamp inside overlay
bubble.moveLeft(std::max(r.left(), std::min(bubble.left(), r.right() - bubbleSize.width())));
bubble.moveTop(std::max(r.top(), std::min(bubble.top(), r.bottom() - bubbleSize.height())));
bubble.setSize(bubbleSize);
return bubble;
}
void TutorialOverlay::parentResized()
{
if (parentWidget()) {
QRect r = parentWidget()->rect();
QPoint globalTopLeft = parentWidget()->mapToGlobal(QPoint(0, 0));
r.moveTopLeft(globalTopLeft);
setGeometry(r);
}
}
void TutorialOverlay::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, true);
QColor overlay(0, 0, 0, 160);
p.fillRect(rect(), overlay);
QRect hole = targetRectOnOverlay().adjusted(-6, -6, 6, 6);
if (!hole.isEmpty()) {
QPainterPath path;
path.addRect(rect());
QPainterPath holePath;
holePath.addRoundedRect(hole, 8, 8);
path = path.subtracted(holePath);
p.setCompositionMode(QPainter::CompositionMode_Clear);
p.fillPath(holePath, Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
}
// recompute bubble size/position in case available geometry changed:
bubble->adjustSize();
QSize bsize = bubble->sizeHint();
const QSize minSize(160, 60);
if (bsize.width() < minSize.width())
bsize.setWidth(minSize.width());
if (bsize.height() < minSize.height())
bsize.setHeight(minSize.height());
highlightBubbleRect = computeBubbleRect(hole, bsize);
bubble->setGeometry(highlightBubbleRect);
bubble->raise();
bubble->show();
}

View File

@@ -0,0 +1,42 @@
#ifndef COCKATRICE_TUTORIAL_OVERLAY_H
#define COCKATRICE_TUTORIAL_OVERLAY_H
#include "tutorial_bubble_widget.h"
#include <QPointer>
#include <QWidget>
class TutorialOverlay : public QWidget
{
Q_OBJECT
public:
explicit TutorialOverlay(QWidget *parent = nullptr);
void setTargetWidget(QWidget *w);
void setText(const QString &t);
void parentResized();
signals:
void nextStep();
void prevStep();
void nextSequence();
void prevSequence();
void skipTutorial();
protected:
void paintEvent(QPaintEvent *) override;
void resizeEvent(QResizeEvent *) override;
void showEvent(QShowEvent *) override;
private:
QRect targetRectOnOverlay() const;
QRect computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const;
QPointer<QWidget> targetWidget;
QString tutorialText;
QRect highlightBubbleRect;
BubbleWidget *bubble;
};
#endif // COCKATRICE_TUTORIAL_OVERLAY_H

View File

@@ -5,6 +5,7 @@
#include "../../interface/pixel_map_generator.h"
#include "../../interface/widgets/cards/card_info_frame_widget.h"
#include "../../interface/widgets/deck_analytics/deck_analytics_widget.h"
#include "../../interface/widgets/general/tutorial/tutorial_controller.h"
#include "../../interface/widgets/visual_deck_editor/visual_deck_editor_widget.h"
#include "../tab_deck_editor.h"
#include "../tab_supervisor.h"
@@ -50,6 +51,31 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra
loadLayout();
databaseDisplayDockWidget->setHidden(true);
tutorialController = new TutorialController(this);
auto sequence = TutorialSequence();
sequence.addStep({tabContainer->visualDeckView, "View your deck here.",
[this]() { tabContainer->setCurrentWidget(tabContainer->visualDeckView); }});
sequence.addStep({printingSelectorDockWidget, "Change the printings in your deck here."});
tutorialController->addSequence(sequence);
auto vddSequence = tabContainer->visualDatabaseDisplay->addTutorialSteps();
vddSequence.steps.prepend({tabContainer->visualDatabaseDisplay, "View the database here",
[this]() { tabContainer->setCurrentWidget(tabContainer->visualDatabaseDisplay); }});
tutorialController->addSequence(vddSequence);
}
void TabDeckEditorVisual::showEvent(QShowEvent *ev)
{
QWidget::showEvent(ev);
if (!tutorialStarted) {
tutorialStarted = true;
// Start on next event loop iteration so everything is fully painted
QTimer::singleShot(0, tutorialController, [this] { tutorialController->start(); });
}
}
/** @brief Creates the central frame containing the tab container. */

View File

@@ -4,6 +4,7 @@
#include "../tab.h"
#include "tab_deck_editor_visual_tab_widget.h"
class TutorialController;
/**
* @class TabDeckEditorVisual
* @ingroup DeckEditorTabs
@@ -55,7 +56,12 @@ class TabDeckEditorVisual : public AbstractTabDeckEditor
{
Q_OBJECT
private:
TutorialController *tutorialController = nullptr;
bool tutorialStarted = false;
protected slots:
void showEvent(QShowEvent *ev) override;
/**
* @brief Load the editor layout from settings.
*/

View File

@@ -5,6 +5,7 @@
#include "../../../filters/syntax_help.h"
#include "../../pixel_map_generator.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../general/tutorial/tutorial_controller.h"
#include "../quick_settings/settings_button_widget.h"
#include "../utility/custom_line_edit.h"
#include "visual_database_display_color_filter_widget.h"
@@ -192,6 +193,28 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
retranslateUi();
}
TutorialSequence VisualDatabaseDisplayWidget::addTutorialSteps()
{
auto sequence = TutorialSequence();
sequence.addStep({colorFilterWidget, "Filter the database by colors with these controls"});
sequence.addStep({displayModeButton, "You can change back to the old table display-style with this button."});
sequence.addStep({filterContainer, "Use these controls for quick access to common filters."});
sequence.addStep(
{quickFilterSaveLoadWidget, "This button will let you save and load all currently applied filters to files."});
sequence.addStep({quickFilterNameWidget,
"This button will let you apply name filters. Optionally, you can import every card in "
"your deck as a name filter and then save this as a filter using the save/load button "
"to make your own quick access collections!"});
sequence.addStep({mainTypeFilterWidget, "Use these buttons to quickly filter by card types."});
sequence.addStep({quickFilterSubTypeWidget, "This button will let you apply filters for card sub-types."});
sequence.addStep(
{quickFilterSetWidget,
"This button will let you apply filters for card sets. You can also filter to the X most recent sets. "
"Filtering to a set will display all printings of a card within that set."});
return sequence;
}
void VisualDatabaseDisplayWidget::initialize()
{
databaseLoadIndicator->setVisible(false);

View File

@@ -14,6 +14,7 @@
#include "../cards/card_size_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "../general/layout_containers/overlap_control_widget.h"
#include "../general/tutorial/tutorial_controller.h"
#include "../utility/custom_line_edit.h"
#include "visual_database_display_color_filter_widget.h"
#include "visual_database_display_filter_save_load_widget.h"
@@ -30,6 +31,7 @@
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <qscrollarea.h>
class TutorialController;
inline Q_LOGGING_CATEGORY(VisualDatabaseDisplayLog, "visual_database_display");
class VisualDatabaseDisplayWidget : public QWidget
@@ -59,6 +61,7 @@ public:
VisualDatabaseDisplayColorFilterWidget *colorFilterWidget;
public slots:
TutorialSequence addTutorialSteps();
void searchModelChanged();
signals: