mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-01-17 15:32:11 -08:00
Compare commits
10 Commits
dependabot
...
first-run-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b718c28dd5 | ||
|
|
b20d0bcb4f | ||
|
|
2c2c2a639b | ||
|
|
f7fe686634 | ||
|
|
6d44b00260 | ||
|
|
fdb547c0e2 | ||
|
|
fe61f5c7d4 | ||
|
|
52547bbfe8 | ||
|
|
9ab398f08d | ||
|
|
0deaa9d9b4 |
@@ -197,6 +197,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
|
||||
|
||||
@@ -459,12 +459,15 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
|
||||
}
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex)
|
||||
void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex, bool preserveWidgetFocus)
|
||||
{
|
||||
deckView->clearSelection();
|
||||
deckView->setCurrentIndex(newCardIndex);
|
||||
recursiveExpand(newCardIndex);
|
||||
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
|
||||
if (!preserveWidgetFocus) {
|
||||
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
|
||||
|
||||
@@ -100,7 +100,7 @@ private slots:
|
||||
void writeComments();
|
||||
void writeBannerCard(int);
|
||||
void applyActiveGroupCriteria();
|
||||
void setSelectedIndex(const QModelIndex &newCardIndex);
|
||||
void setSelectedIndex(const QModelIndex &newCardIndex, bool preserveWidgetFocus);
|
||||
void updateHash();
|
||||
void refreshShortcuts();
|
||||
void updateShowBannerCardComboBox(bool visible);
|
||||
|
||||
@@ -182,7 +182,7 @@ QModelIndex DeckStateManager::addCard(const ExactCard &card, const QString &zone
|
||||
QModelIndex idx = modifyDeck(reason, [&card, &zone](auto model) { return model->addCard(card, zone); });
|
||||
|
||||
if (idx.isValid()) {
|
||||
emit focusIndexChanged(idx);
|
||||
emit focusIndexChanged(idx, true);
|
||||
}
|
||||
|
||||
return idx;
|
||||
@@ -208,7 +208,7 @@ QModelIndex DeckStateManager::decrementCard(const ExactCard &card, const QString
|
||||
}
|
||||
|
||||
if (idx.isValid()) {
|
||||
emit focusIndexChanged(idx);
|
||||
emit focusIndexChanged(idx, true);
|
||||
}
|
||||
|
||||
return idx;
|
||||
|
||||
@@ -290,8 +290,9 @@ signals:
|
||||
/**
|
||||
* The selected card on any views connected to this deck should be changed to this index.
|
||||
* @param index The model index
|
||||
* @param preserveWidgetFocus Whether to keep the widget focus unchanged
|
||||
*/
|
||||
void focusIndexChanged(QModelIndex index);
|
||||
void focusIndexChanged(QModelIndex index, bool preserveWidgetFocus);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_DECK_STATE_MANAGER_H
|
||||
@@ -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>
|
||||
@@ -44,6 +45,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()
|
||||
@@ -184,29 +209,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(LoadedDeck()); });
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
DeckList backgroundSourceDeck;
|
||||
QPixmap overlay;
|
||||
QPair<QColor, QColor> gradientColors;
|
||||
HomeStyledButton *connectButton;
|
||||
|
||||
void loadBackgroundSourceDeck();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "tutorial_bubble_widget.h"
|
||||
|
||||
BubbleWidget::BubbleWidget(QWidget *parent) : QFrame(parent)
|
||||
{
|
||||
setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
||||
setStyleSheet("QFrame { background:white; border-radius:8px; }"
|
||||
"QLabel { color:black; }");
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
// Layout
|
||||
layout->addWidget(counterLabel, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
|
||||
layout->addItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 1);
|
||||
layout->addWidget(textLabel, 1, 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();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#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;
|
||||
|
||||
BubbleWidget(QWidget *parent);
|
||||
void setText(const QString &text);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,261 @@
|
||||
#include "tutorial_overlay.h"
|
||||
|
||||
#include "tutorial_bubble_widget.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
|
||||
TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent, Qt::Window)
|
||||
{
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
|
||||
if (parent) {
|
||||
parent->installEventFilter(this);
|
||||
parentResized();
|
||||
}
|
||||
|
||||
// ---- Control bar -------------------------------------------------
|
||||
controlBar = new QFrame(this);
|
||||
controlBar->setStyleSheet(
|
||||
"QFrame { background: rgba(30,30,30,200); border-radius: 6px; }"
|
||||
"QPushButton { padding: 6px 10px; border: 1px solid #aaa; border-radius: 4px; background:#f5f5f5; }"
|
||||
"QPushButton:hover { background:#eaeaea; }");
|
||||
|
||||
QHBoxLayout *barLayout = new QHBoxLayout(controlBar);
|
||||
barLayout->setContentsMargins(8, 4, 8, 4);
|
||||
|
||||
titleLabel = new QLabel("Tutorial", controlBar);
|
||||
titleLabel->setStyleSheet("color:white; font-weight:bold;");
|
||||
barLayout->addWidget(titleLabel);
|
||||
barLayout->addStretch();
|
||||
|
||||
auto mkBtn = [&](const QString &t, const QString &tip) {
|
||||
QPushButton *b = new QPushButton(t, controlBar);
|
||||
b->setToolTip(tip);
|
||||
return b;
|
||||
};
|
||||
|
||||
QPushButton *prevSeq = mkBtn("⏮", "Previous chapter");
|
||||
QPushButton *prev = mkBtn("◀", "Previous step");
|
||||
QPushButton *next = mkBtn("▶", "Next step");
|
||||
QPushButton *nextSeq = mkBtn("⏭", "Next chapter");
|
||||
QPushButton *close = mkBtn("✕", "Exit tutorial");
|
||||
|
||||
barLayout->addWidget(prevSeq);
|
||||
barLayout->addWidget(prev);
|
||||
barLayout->addWidget(next);
|
||||
barLayout->addWidget(nextSeq);
|
||||
barLayout->addWidget(close);
|
||||
|
||||
connect(prev, &QPushButton::clicked, this, &TutorialOverlay::prevStep);
|
||||
connect(next, &QPushButton::clicked, this, &TutorialOverlay::nextStep);
|
||||
connect(prevSeq, &QPushButton::clicked, this, &TutorialOverlay::prevSequence);
|
||||
connect(nextSeq, &QPushButton::clicked, this, &TutorialOverlay::nextSequence);
|
||||
connect(close, &QPushButton::clicked, this, &TutorialOverlay::skipTutorial);
|
||||
|
||||
// ---- Bubble ------------------------------------------------------
|
||||
bubble = new BubbleWidget(this);
|
||||
bubble->hide();
|
||||
|
||||
controlBar->hide();
|
||||
}
|
||||
|
||||
void TutorialOverlay::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
|
||||
// Queue geometry correction after the window is fully shown
|
||||
QMetaObject::invokeMethod(this, [this]() { parentResized(); }, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void TutorialOverlay::setTitle(const QString &title)
|
||||
{
|
||||
titleLabel->setText(title);
|
||||
}
|
||||
|
||||
void TutorialOverlay::setBlocking(bool block)
|
||||
{
|
||||
blockInput = block;
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, !blockInput);
|
||||
}
|
||||
|
||||
void TutorialOverlay::setTargetWidget(QWidget *w)
|
||||
{
|
||||
if (targetWidget)
|
||||
targetWidget->removeEventFilter(this);
|
||||
|
||||
targetWidget = w;
|
||||
|
||||
if (targetWidget)
|
||||
targetWidget->installEventFilter(this);
|
||||
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
void TutorialOverlay::setText(const QString &t)
|
||||
{
|
||||
tutorialText = t;
|
||||
bubble->setText(t);
|
||||
bubble->adjustSize();
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
QRect TutorialOverlay::currentHoleRect() const
|
||||
{
|
||||
if (!targetWidget || !targetWidget->isVisible())
|
||||
return QRect();
|
||||
|
||||
QPoint globalTopLeft = targetWidget->mapToGlobal(QPoint(0, 0));
|
||||
QPoint localTopLeft = mapFromGlobal(globalTopLeft);
|
||||
return QRect(localTopLeft, targetWidget->size()).adjusted(-6, -6, 6, 6);
|
||||
}
|
||||
|
||||
void TutorialOverlay::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
bool TutorialOverlay::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (obj == parentWidget() && (event->type() == QEvent::Resize || event->type() == QEvent::Move)) {
|
||||
parentResized();
|
||||
}
|
||||
|
||||
if (obj == targetWidget) {
|
||||
if (event->type() == QEvent::Show) {
|
||||
// Defer layout recalculation to give Qt time to finalize geometry
|
||||
QMetaObject::invokeMethod(this, [this]() { recomputeLayout(); }, Qt::QueuedConnection);
|
||||
} else if (event->type() == QEvent::Hide || event->type() == QEvent::Move || event->type() == QEvent::Resize) {
|
||||
recomputeLayout();
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void TutorialOverlay::parentResized()
|
||||
{
|
||||
if (!parentWidget())
|
||||
return;
|
||||
|
||||
QWidget *w = parentWidget();
|
||||
|
||||
// Get the parent rect in local coordinates
|
||||
QRect r = w->rect();
|
||||
|
||||
// Map top-left to global coordinates
|
||||
QPoint globalTopLeft = w->mapToGlobal(QPoint(0, 0));
|
||||
|
||||
// Set overlay geometry in screen coords, exactly matching the parent widget
|
||||
r.moveTopLeft(globalTopLeft);
|
||||
setGeometry(r);
|
||||
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
void TutorialOverlay::recomputeLayout()
|
||||
{
|
||||
QRect hole = currentHoleRect();
|
||||
|
||||
if (hole.isEmpty()) {
|
||||
if (bubble) {
|
||||
bubble->hide();
|
||||
}
|
||||
if (controlBar) {
|
||||
controlBar->hide();
|
||||
}
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
show();
|
||||
raise();
|
||||
|
||||
// ---- Bubble ----
|
||||
QSize bsize = bubble->sizeHint().expandedTo(QSize(160, 60));
|
||||
highlightBubbleRect = computeBubbleRect(hole, bsize);
|
||||
bubble->setGeometry(highlightBubbleRect);
|
||||
bubble->show();
|
||||
bubble->raise();
|
||||
|
||||
// ---- Control bar ----
|
||||
controlBar->adjustSize();
|
||||
controlBar->show();
|
||||
|
||||
const int margin = 8;
|
||||
QRect r = rect();
|
||||
|
||||
QList<QPoint> positions = {{r.right() - controlBar->width() - margin, r.bottom() - controlBar->height() - margin},
|
||||
{r.right() - controlBar->width() - margin, margin},
|
||||
{margin, r.bottom() - controlBar->height() - margin},
|
||||
{margin, margin}};
|
||||
|
||||
for (const QPoint &pos : positions) {
|
||||
QRect proposed(pos, controlBar->size());
|
||||
if (!proposed.intersects(hole)) {
|
||||
controlBar->move(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
controlBar->raise();
|
||||
update();
|
||||
}
|
||||
|
||||
QRect TutorialOverlay::computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const
|
||||
{
|
||||
const int margin = 16;
|
||||
QRect r = rect();
|
||||
QRect bubble;
|
||||
|
||||
if (hole.isEmpty()) {
|
||||
bubble = QRect(r.center() - QPoint(bubbleSize.width() / 2, bubbleSize.height() / 2), bubbleSize);
|
||||
} else {
|
||||
bubble = QRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
|
||||
|
||||
if (!r.contains(bubble))
|
||||
bubble.moveLeft(hole.left() - margin - bubbleSize.width());
|
||||
|
||||
if (!r.contains(bubble)) {
|
||||
bubble.moveLeft(hole.center().x() - bubbleSize.width() / 2);
|
||||
bubble.moveTop(hole.top() - margin - bubbleSize.height());
|
||||
}
|
||||
|
||||
if (!r.contains(bubble))
|
||||
bubble.moveTop(hole.bottom() + margin);
|
||||
}
|
||||
|
||||
// Final clamp to overlay bounds - ensure min <= max for qBound
|
||||
int maxLeft = qMax(r.left(), r.right() - bubble.width());
|
||||
int maxTop = qMax(r.top(), r.bottom() - bubble.height());
|
||||
|
||||
bubble.moveLeft(qBound(r.left(), bubble.left(), maxLeft));
|
||||
bubble.moveTop(qBound(r.top(), bubble.top(), maxTop));
|
||||
|
||||
return bubble;
|
||||
}
|
||||
|
||||
void TutorialOverlay::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
p.fillRect(rect(), QColor(0, 0, 0, 160));
|
||||
|
||||
QRect hole = currentHoleRect();
|
||||
if (!hole.isEmpty()) {
|
||||
QPainterPath holePath;
|
||||
holePath.addRoundedRect(hole, 8, 8);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Clear);
|
||||
p.fillPath(holePath, Qt::transparent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef COCKATRICE_TUTORIAL_OVERLAY_H
|
||||
#define COCKATRICE_TUTORIAL_OVERLAY_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class BubbleWidget;
|
||||
class QLabel;
|
||||
class QFrame;
|
||||
|
||||
class TutorialOverlay : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
public:
|
||||
explicit TutorialOverlay(QWidget *parent = nullptr);
|
||||
|
||||
void setTargetWidget(QWidget *w);
|
||||
void updateHoleRect();
|
||||
void setText(const QString &t);
|
||||
QRect currentHoleRect() const;
|
||||
void setTitle(const QString &title);
|
||||
void setBlocking(bool blockInput);
|
||||
|
||||
void parentResized();
|
||||
|
||||
signals:
|
||||
void nextStep();
|
||||
void prevStep();
|
||||
void nextSequence();
|
||||
void prevSequence();
|
||||
void skipTutorial();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void resizeEvent(QResizeEvent *) override;
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private:
|
||||
QRect computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const;
|
||||
void recomputeLayout();
|
||||
|
||||
QWidget *targetWidget = nullptr;
|
||||
|
||||
BubbleWidget *bubble = nullptr;
|
||||
QFrame *controlBar = nullptr;
|
||||
|
||||
QLabel *titleLabel = nullptr;
|
||||
|
||||
QString tutorialText;
|
||||
|
||||
QRect cachedHoleRect;
|
||||
QRect highlightBubbleRect;
|
||||
|
||||
bool blockInput = true;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_TUTORIAL_OVERLAY_H
|
||||
@@ -198,7 +198,7 @@ void CardAmountWidget::addPrinting(const QString &zone)
|
||||
});
|
||||
|
||||
if (newCardIndex.isValid()) {
|
||||
emit deckStateManager->focusIndexChanged(newCardIndex);
|
||||
emit deckStateManager->focusIndexChanged(newCardIndex, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,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,7 +51,31 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra
|
||||
refreshShortcuts();
|
||||
|
||||
loadLayout();
|
||||
cardDatabaseDockWidget->setHidden(true);
|
||||
cardDatabaseDockWidget->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. */
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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"
|
||||
@@ -194,6 +195,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);
|
||||
|
||||
@@ -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"
|
||||
@@ -31,6 +32,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
|
||||
@@ -60,6 +62,7 @@ public:
|
||||
VisualDatabaseDisplayColorFilterWidget *colorFilterWidget;
|
||||
|
||||
public slots:
|
||||
TutorialSequence addTutorialSteps();
|
||||
void searchModelChanged();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
@page user_reference User Reference
|
||||
|
||||
- @subpage search_syntax_help
|
||||
- @subpage deck_search_syntax_help
|
||||
|
||||
# Deck Management
|
||||
|
||||
- @subpage creating_decks
|
||||
- @subpage importing_decks
|
||||
- @subpage editing_decks
|
||||
- @subpage exporting_decks
|
||||
|
||||
# Release Channels
|
||||
|
||||
- @subpage beta_release
|
||||
|
||||
# Syntax Help
|
||||
|
||||
- @subpage search_syntax_help
|
||||
- @subpage deck_search_syntax_help
|
||||
54
doc/doxygen/extra-pages/user_documentation/settings/beta.md
Normal file
54
doc/doxygen/extra-pages/user_documentation/settings/beta.md
Normal file
@@ -0,0 +1,54 @@
|
||||
@page beta_release Beta Release
|
||||
|
||||
The beta version of Cockatrice features all the newest features before they make their way into the latest stable
|
||||
release.
|
||||
It allows us to test these features ahead of time and you to give feedback on them.
|
||||
We release new betas very frequently and usually release a follow-up beta with a fix within a day or two if we break
|
||||
anything. In contrast, stable releases are released much less frequently but ensured to be stable.
|
||||
|
||||
# What to Expect from the Beta
|
||||
|
||||
Using the beta and giving feedback on it helps us improve Cockatrice a lot.
|
||||
We do not perform any automatic data collection so your voluntary feedback is the only way for us to get a sense of what
|
||||
you enjoy, expect, or would like to change.
|
||||
|
||||
We highly appreciate your active participation and feedback.
|
||||
|
||||
That being said, some words of warning should be issued:
|
||||
|
||||
The beta release may contain unfinished features, bugs, or performance issues.
|
||||
It is intended for testing and feedback and may be less stable than the default release.
|
||||
|
||||
We recommend using the beta only if you are comfortable encountering issues and helping us improve Cockatrice.
|
||||
|
||||
# Switching to the Beta
|
||||
|
||||
To switch to the Beta, navigate to your settings, using the Tab "Cockatrice → Settings" or Ctrl + Shift + P (default
|
||||
shortcut).
|
||||
|
||||
Within the settings, in the first tab 'General', the first section 'Personal Settings' contains the setting 'Release
|
||||
Channel'.
|
||||
|
||||
\image html release_channel_beta.png
|
||||
|
||||
Switch this to 'Beta'.
|
||||
|
||||
Afterwards, close your settings, and use the 'Help' tab to search for a client update. You should be prompted to install
|
||||
the beta version.
|
||||
|
||||
# Switching back to Stable
|
||||
|
||||
Follow the same steps as above, but set Release Channel to 'Default'.
|
||||
|
||||
# Giving Feedback
|
||||
|
||||
If you encounter a bug or unexpected behavior while using the beta, please report it on:
|
||||
|
||||
- GitHub: https://github.com/Cockatrice/Cockatrice/issues
|
||||
- Discord: https://discord.gg/4hNswvHeFd in #beta-testing
|
||||
|
||||
When reporting issues, please include:
|
||||
|
||||
- Your operating system
|
||||
- The beta version number (found in 'Help → About Cockatrice')
|
||||
- Steps to reproduce the problem, if possible
|
||||
BIN
doc/doxygen/images/release_channel_beta.png
Normal file
BIN
doc/doxygen/images/release_channel_beta.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
@@ -2,22 +2,22 @@
|
||||
<context>
|
||||
<name>IntroPage</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="215"/>
|
||||
<location filename="src/pages.cpp" line="127"/>
|
||||
<source>Introduction</source>
|
||||
<translation>Einführung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="216"/>
|
||||
<location filename="src/pages.cpp" line="128"/>
|
||||
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.</source>
|
||||
<translation>Dieser Assistent wird eine Liste aller Editionen, Karten und Spielsteine importieren, die von Cockatrice genutzt werden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="218"/>
|
||||
<location filename="src/pages.cpp" line="130"/>
|
||||
<source>Interface language:</source>
|
||||
<translation>Interfacesprache:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="219"/>
|
||||
<location filename="src/pages.cpp" line="131"/>
|
||||
<source>Version:</source>
|
||||
<translation>Version:</translation>
|
||||
</message>
|
||||
@@ -25,134 +25,134 @@
|
||||
<context>
|
||||
<name>LoadSetsPage</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="288"/>
|
||||
<location filename="src/pages.cpp" line="200"/>
|
||||
<source>Source selection</source>
|
||||
<translation>Quellenauswahl</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="289"/>
|
||||
<location filename="src/pages.cpp" line="201"/>
|
||||
<source>Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
|
||||
<translation>Bitte geben Sie eine kompatible Quelle für die Liste der Editionen und Karten an. Sie können eine URL-Adresse zum Herunterladen angeben oder eine existierende Datei von Ihrem Computer verwenden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="293"/>
|
||||
<location filename="src/pages.cpp" line="205"/>
|
||||
<source>Download URL:</source>
|
||||
<translation>Download URL:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="294"/>
|
||||
<location filename="src/pages.cpp" line="206"/>
|
||||
<source>Local file:</source>
|
||||
<translation>Lokale Datei:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="295"/>
|
||||
<location filename="src/pages.cpp" line="207"/>
|
||||
<source>Restore default URL</source>
|
||||
<translation>Standard-URL wiederherstellen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="296"/>
|
||||
<location filename="src/pages.cpp" line="208"/>
|
||||
<source>Choose file...</source>
|
||||
<translation>Datei auswählen...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="306"/>
|
||||
<location filename="src/pages.cpp" line="218"/>
|
||||
<source>Load sets file</source>
|
||||
<translation>Editionsdatei wird geladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="316"/>
|
||||
<location filename="src/pages.cpp" line="228"/>
|
||||
<source>Sets file (%1)</source>
|
||||
<oldsource>Sets JSON file (%1)</oldsource>
|
||||
<translation>Sets Datei (%1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="346"/>
|
||||
<location filename="src/oraclewizard.cpp" line="365"/>
|
||||
<location filename="src/oraclewizard.cpp" line="370"/>
|
||||
<location filename="src/oraclewizard.cpp" line="438"/>
|
||||
<location filename="src/oraclewizard.cpp" line="569"/>
|
||||
<location filename="src/oraclewizard.cpp" line="582"/>
|
||||
<location filename="src/oraclewizard.cpp" line="603"/>
|
||||
<location filename="src/pages.cpp" line="258"/>
|
||||
<location filename="src/pages.cpp" line="277"/>
|
||||
<location filename="src/pages.cpp" line="282"/>
|
||||
<location filename="src/pages.cpp" line="354"/>
|
||||
<location filename="src/pages.cpp" line="485"/>
|
||||
<location filename="src/pages.cpp" line="498"/>
|
||||
<location filename="src/pages.cpp" line="519"/>
|
||||
<source>Error</source>
|
||||
<translation>Fehler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="346"/>
|
||||
<location filename="src/pages.cpp" line="258"/>
|
||||
<source>The provided URL is not valid.</source>
|
||||
<translation>Die eingegebene URL ist nicht gültig.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="350"/>
|
||||
<location filename="src/pages.cpp" line="262"/>
|
||||
<source>Downloading (0MB)</source>
|
||||
<translation>Herunterladen (0MB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="365"/>
|
||||
<location filename="src/pages.cpp" line="277"/>
|
||||
<source>Please choose a file.</source>
|
||||
<translation>Bitte wählen Sie eine Datei.</translation>
|
||||
<translation>Bitte wähle eine Datei aus.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="370"/>
|
||||
<location filename="src/pages.cpp" line="282"/>
|
||||
<source>Cannot open file '%1'.</source>
|
||||
<translation>Datei '%1' kann nicht geöffnet werden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="429"/>
|
||||
<location filename="src/pages.cpp" line="345"/>
|
||||
<source>Downloading (%1MB)</source>
|
||||
<translation>Herunterladen (%1MB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="438"/>
|
||||
<location filename="src/pages.cpp" line="354"/>
|
||||
<source>Network error: %1.</source>
|
||||
<translation>Netzwerkfehler: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="476"/>
|
||||
<location filename="src/pages.cpp" line="392"/>
|
||||
<source>Parsing file</source>
|
||||
<translation>Datei wird verarbeitet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="498"/>
|
||||
<location filename="src/pages.cpp" line="414"/>
|
||||
<source>Xz extraction failed.</source>
|
||||
<translation>Fehler beim Extrahieren der xz Datei.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="505"/>
|
||||
<location filename="src/pages.cpp" line="421"/>
|
||||
<source>Sorry, this version of Oracle does not support xz compressed files.</source>
|
||||
<translation>Es tut uns Leid, diese Version von Oracle unterstützt keine xz komprimierten Dateien.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="525"/>
|
||||
<location filename="src/pages.cpp" line="441"/>
|
||||
<source>Failed to open Zip archive: %1.</source>
|
||||
<translation>Fehler beim Öffnen des Zip Archivs: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="530"/>
|
||||
<location filename="src/pages.cpp" line="446"/>
|
||||
<source>Zip extraction failed: the Zip archive doesn't contain exactly one file.</source>
|
||||
<translation>Fehler beim Extrahieren: Das Zip Archiv enthält mehr als eine Datei.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="538"/>
|
||||
<location filename="src/pages.cpp" line="454"/>
|
||||
<source>Zip extraction failed: %1.</source>
|
||||
<translation>Fehler beim Extrahieren: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="546"/>
|
||||
<location filename="src/pages.cpp" line="462"/>
|
||||
<source>Sorry, this version of Oracle does not support zipped files.</source>
|
||||
<translation>Es tut uns Leid, diese Version von Oracle unterstützt keine Zip Archive.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="569"/>
|
||||
<location filename="src/pages.cpp" line="485"/>
|
||||
<source>Failed to interpret downloaded data.</source>
|
||||
<translation>Interpretation der heruntergeladenen Daten fehlgeschlagen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="582"/>
|
||||
<location filename="src/pages.cpp" line="498"/>
|
||||
<source>Do you want to download the uncompressed file instead?</source>
|
||||
<translation>Möchten Sie stattdessen die unkomprimierte Datei herunterladen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="604"/>
|
||||
<location filename="src/pages.cpp" line="520"/>
|
||||
<source>The file was retrieved successfully, but it does not contain any sets data.</source>
|
||||
<translation>Die Datei wurde erfolgreich abgerufen, sie enthält aber keine Editionsdaten.</translation>
|
||||
</message>
|
||||
@@ -160,42 +160,57 @@
|
||||
<context>
|
||||
<name>LoadSpoilersPage</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="790"/>
|
||||
<location filename="src/pages.cpp" line="716"/>
|
||||
<source>Save spoiler database</source>
|
||||
<translation>Speichere Spoilerdatenbank</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="795"/>
|
||||
<location filename="src/pages.cpp" line="721"/>
|
||||
<source>XML; spoiler database (*.xml)</source>
|
||||
<translation>XML; Spoilerdatenbank (*.xml)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="800"/>
|
||||
<location filename="src/pages.cpp" line="726"/>
|
||||
<source>spoiler</source>
|
||||
<translation>Spoiler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pages.cpp" line="731"/>
|
||||
<source>Spoilers import</source>
|
||||
<translation>Spoilerimport</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="801"/>
|
||||
<location filename="src/pages.cpp" line="732"/>
|
||||
<source>Please specify a compatible source for spoiler data.</source>
|
||||
<translation>Bitte geben Sie eine kompatible Quelle für Spoilerdaten an.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="803"/>
|
||||
<location filename="src/pages.cpp" line="734"/>
|
||||
<source>Download URL:</source>
|
||||
<translation>Download URL:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="804"/>
|
||||
<location filename="src/pages.cpp" line="735"/>
|
||||
<source>Local file:</source>
|
||||
<translation>Lokale Datei:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pages.cpp" line="736"/>
|
||||
<source>Restore default URL</source>
|
||||
<translation>Standard-URL wiederherstellen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="805"/>
|
||||
<location filename="src/pages.cpp" line="737"/>
|
||||
<source>Choose file...</source>
|
||||
<translation>Datei auswählen...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pages.cpp" line="739"/>
|
||||
<source>The spoiler database will be saved at the following location:</source>
|
||||
<translation>Die Spoilerdatenbank wird in folgendem Pfad gespeichert:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="807"/>
|
||||
<location filename="src/pages.cpp" line="741"/>
|
||||
<source>Save to a custom path (not recommended)</source>
|
||||
<translation>Speichere in benutzerdefiniertem Pfad (nicht empfohlen)</translation>
|
||||
</message>
|
||||
@@ -203,42 +218,57 @@
|
||||
<context>
|
||||
<name>LoadTokensPage</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="753"/>
|
||||
<location filename="src/pages.cpp" line="671"/>
|
||||
<source>Save token database</source>
|
||||
<translation>Speichere Spielsteindatenbank</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="758"/>
|
||||
<location filename="src/pages.cpp" line="676"/>
|
||||
<source>XML; token database (*.xml)</source>
|
||||
<translation>XML; Spielsteindatenbank (*.xml)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="763"/>
|
||||
<location filename="src/pages.cpp" line="681"/>
|
||||
<source>tokens</source>
|
||||
<translation>Spielsteine</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pages.cpp" line="686"/>
|
||||
<source>Tokens import</source>
|
||||
<translation>Spielsteinimport</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="764"/>
|
||||
<location filename="src/pages.cpp" line="687"/>
|
||||
<source>Please specify a compatible source for token data.</source>
|
||||
<translation>Bitte geben Sie eine kompatible Quelle für Spielsteindaten an.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="766"/>
|
||||
<location filename="src/pages.cpp" line="689"/>
|
||||
<source>Download URL:</source>
|
||||
<translation>Download URL:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="767"/>
|
||||
<location filename="src/pages.cpp" line="690"/>
|
||||
<source>Local file:</source>
|
||||
<translation>Lokale Datei:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pages.cpp" line="691"/>
|
||||
<source>Restore default URL</source>
|
||||
<translation>Standard-URL wiederherstellen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="768"/>
|
||||
<location filename="src/pages.cpp" line="692"/>
|
||||
<source>Choose file...</source>
|
||||
<translation>Datei auswählen...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pages.cpp" line="694"/>
|
||||
<source>The token database will be saved at the following location:</source>
|
||||
<translation>Die Spielsteindatenbank wird in folgendem Pfad gespeichert:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="770"/>
|
||||
<location filename="src/pages.cpp" line="696"/>
|
||||
<source>Save to a custom path (not recommended)</source>
|
||||
<translation>Speichere in benutzerdefiniertem Pfad (nicht empfohlen)</translation>
|
||||
</message>
|
||||
@@ -246,7 +276,7 @@
|
||||
<context>
|
||||
<name>OracleImporter</name>
|
||||
<message>
|
||||
<location filename="src/oracleimporter.cpp" line="466"/>
|
||||
<location filename="src/oracleimporter.cpp" line="541"/>
|
||||
<source>Dummy set containing tokens</source>
|
||||
<translation>Platzhalter Edition mit Spielsteinen</translation>
|
||||
</message>
|
||||
@@ -254,7 +284,7 @@
|
||||
<context>
|
||||
<name>OracleWizard</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="106"/>
|
||||
<location filename="src/oraclewizard.cpp" line="70"/>
|
||||
<source>Oracle Importer</source>
|
||||
<translation>Oracle Importer</translation>
|
||||
</message>
|
||||
@@ -262,22 +292,22 @@
|
||||
<context>
|
||||
<name>OutroPage</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="224"/>
|
||||
<location filename="src/pages.cpp" line="136"/>
|
||||
<source>Finished</source>
|
||||
<translation>Fertig</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="225"/>
|
||||
<location filename="src/pages.cpp" line="137"/>
|
||||
<source>The wizard has finished.</source>
|
||||
<translation>Der Wizard ist fertig.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="226"/>
|
||||
<location filename="src/pages.cpp" line="138"/>
|
||||
<source>You can now start using Cockatrice with the newly updated cards.</source>
|
||||
<translation>Sie können nun Cockatrice mit den aktuellen Karten verwenden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="227"/>
|
||||
<location filename="src/pages.cpp" line="139"/>
|
||||
<source>If the card databases don't reload automatically, restart the Cockatrice client.</source>
|
||||
<translation>Falls die Datenbanken nicht automatisch neu geladen werden, starten Sie bitte Cockatrice neu.</translation>
|
||||
</message>
|
||||
@@ -285,73 +315,73 @@
|
||||
<context>
|
||||
<name>SaveSetsPage</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="646"/>
|
||||
<location filename="src/oraclewizard.cpp" line="720"/>
|
||||
<location filename="src/pages.cpp" line="563"/>
|
||||
<location filename="src/pages.cpp" line="638"/>
|
||||
<source>Error</source>
|
||||
<translation>Fehler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="646"/>
|
||||
<location filename="src/pages.cpp" line="563"/>
|
||||
<source>No set has been imported.</source>
|
||||
<translation>Es wurden keine Editionen importiert.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="656"/>
|
||||
<location filename="src/pages.cpp" line="574"/>
|
||||
<source>Sets imported</source>
|
||||
<translation>Editionen wurden importiert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="658"/>
|
||||
<location filename="src/pages.cpp" line="576"/>
|
||||
<source>A cockatrice database file of %1 MB has been downloaded.</source>
|
||||
<translation>Eine Cockatrice-Datenbankdatei der Größe %1 MB wurde heruntergeladen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="661"/>
|
||||
<location filename="src/pages.cpp" line="579"/>
|
||||
<source>The following sets have been found:</source>
|
||||
<translation>Die folgenden Sets wurden gefunden:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="664"/>
|
||||
<location filename="src/pages.cpp" line="582"/>
|
||||
<source>Press "Save" to store the imported cards in the Cockatrice database.</source>
|
||||
<translation>Drücken Sie "Speichern", um die importierten Karten in der Datenbank zu speichern.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="665"/>
|
||||
<location filename="src/pages.cpp" line="583"/>
|
||||
<source>The card database will be saved at the following location:</source>
|
||||
<translation>Die Kartendatenbank wird in folgendem Pfad gespeichert:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="667"/>
|
||||
<location filename="src/pages.cpp" line="585"/>
|
||||
<source>Save to a custom path (not recommended)</source>
|
||||
<translation>Speichere in benutzerdefiniertem Pfad (nicht empfohlen)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="669"/>
|
||||
<location filename="src/pages.cpp" line="587"/>
|
||||
<source>&Save</source>
|
||||
<translation>&Speichern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="675"/>
|
||||
<location filename="src/pages.cpp" line="593"/>
|
||||
<source>Import finished: %1 cards.</source>
|
||||
<translation>Importieren abgeschlossen: %1 Karten.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="678"/>
|
||||
<location filename="src/pages.cpp" line="596"/>
|
||||
<source>%1: %2 cards imported</source>
|
||||
<translation>%1: %2 Karten importiert.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="687"/>
|
||||
<location filename="src/pages.cpp" line="605"/>
|
||||
<source>Save card database</source>
|
||||
<translation>Kartendatenbank speichern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="688"/>
|
||||
<location filename="src/pages.cpp" line="606"/>
|
||||
<source>XML; card database (*.xml)</source>
|
||||
<translation>XML; Kartendatenbank (*.xml)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="720"/>
|
||||
<location filename="src/pages.cpp" line="638"/>
|
||||
<source>The file could not be saved to %1</source>
|
||||
<translation>Die Datei konnte nicht gespeichert werden:
|
||||
%1 </translation>
|
||||
@@ -360,34 +390,56 @@
|
||||
<context>
|
||||
<name>SimpleDownloadFilePage</name>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="73"/>
|
||||
<location filename="src/pagetemplates.cpp" line="113"/>
|
||||
<location filename="src/pagetemplates.cpp" line="169"/>
|
||||
<location filename="src/pagetemplates.cpp" line="72"/>
|
||||
<source>Load %1 file</source>
|
||||
<translation>Lade %1 Datei</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="82"/>
|
||||
<source>%1 file (%1)</source>
|
||||
<translation>%1 Datei (%1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="111"/>
|
||||
<location filename="src/pagetemplates.cpp" line="129"/>
|
||||
<location filename="src/pagetemplates.cpp" line="134"/>
|
||||
<location filename="src/pagetemplates.cpp" line="168"/>
|
||||
<location filename="src/pagetemplates.cpp" line="224"/>
|
||||
<source>Error</source>
|
||||
<translation>Fehler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="73"/>
|
||||
<location filename="src/pagetemplates.cpp" line="111"/>
|
||||
<source>The provided URL is not valid: </source>
|
||||
<translation>Die bereitgestellte URL ist nicht gültig:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="77"/>
|
||||
<location filename="src/pagetemplates.cpp" line="115"/>
|
||||
<source>Downloading (0MB)</source>
|
||||
<translation>Herunterladen (0MB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="104"/>
|
||||
<location filename="src/pagetemplates.cpp" line="129"/>
|
||||
<source>Please choose a file.</source>
|
||||
<translation>Bitte wähle eine Datei aus.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="134"/>
|
||||
<source>Cannot open file '%1'.</source>
|
||||
<translation>Datei '%1' kann nicht geöffnet werden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="159"/>
|
||||
<source>Downloading (%1MB)</source>
|
||||
<translation>Herunterladen (%1MB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="113"/>
|
||||
<location filename="src/pagetemplates.cpp" line="168"/>
|
||||
<source>Network error: %1.</source>
|
||||
<translation>Netzwerkfehler: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/pagetemplates.cpp" line="169"/>
|
||||
<location filename="src/pagetemplates.cpp" line="224"/>
|
||||
<source>The file could not be saved to %1</source>
|
||||
<translation>Die Datei konnte nicht in %1 gespeichert werden</translation>
|
||||
</message>
|
||||
@@ -536,7 +588,7 @@
|
||||
<context>
|
||||
<name>i18n</name>
|
||||
<message>
|
||||
<location filename="src/oraclewizard.cpp" line="58"/>
|
||||
<location filename="src/oraclewizard.cpp" line="22"/>
|
||||
<source>English</source>
|
||||
<translation>Deutsch (German)</translation>
|
||||
</message>
|
||||
|
||||
Reference in New Issue
Block a user