[PictureLoader] Allow saving downloaded images to local storage and not just the QNetworkManager cache (#6620)

* [PictureLoader] Allow saving downloaded images to local storage and not just the QNetworkManager cache.

Took 1 hour 11 minutes

Took 4 seconds


Took 25 seconds

* Give people options from a dropdown.

Took 1 hour 6 minutes

Took 3 seconds

* Simplify directory removal code.

Took 5 minutes

Took 8 seconds

* Merge pull request #8

* Create new category for new settings

* Split off storage settings

Took 47 minutes

Took 4 seconds

* Allow toggling between caching methods.

Took 1 hour 30 minutes

Took 9 seconds

* Adjust settings dialog.

Took 5 minutes

Took 59 seconds

Took 22 seconds

Took 6 seconds

* tr() strings

Took 1 minute

Took 6 seconds

* Readjust layout, default naming scheme.

Took 5 minutes

* Add stretch.

Took 9 minutes

* Make scrollable.

Took 2 minutes

* Add icon.

Took 7 minutes

* Change naming to be uniform.

Took 3 minutes

Took 3 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
This commit is contained in:
BruebachL 2026-05-16 20:15:10 +02:00 committed by GitHub
parent 20cd7ce73d
commit f8ce5c2e39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1382 additions and 132 deletions

View file

@ -1013,8 +1013,6 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse);
urlLinkLabel.setOpenExternalLinks(true);
connect(&clearDownloadedPicsButton, &QPushButton::clicked, this,
&DeckEditorSettingsPage::clearDownloadedPicsButtonClicked);
connect(&resetDownloadURLs, &QPushButton::clicked, this, &DeckEditorSettingsPage::resetDownloadedURLsButtonClicked);
auto *lpGeneralGrid = new QGridLayout;
@ -1066,49 +1064,11 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
urlListLayout->addWidget(urlToolBar);
urlListLayout->addWidget(urlList);
// pixmap cache
pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN);
// 2047 is the max value to avoid overflowing of QPixmapCache::setCacheLimit(int size)
pixmapCacheEdit.setMaximum(PIXMAPCACHE_SIZE_MAX);
pixmapCacheEdit.setSingleStep(64);
pixmapCacheEdit.setValue(SettingsCache::instance().getPixmapCacheSize());
pixmapCacheEdit.setSuffix(" MB");
networkCacheEdit.setMinimum(NETWORK_CACHE_SIZE_MIN);
networkCacheEdit.setMaximum(NETWORK_CACHE_SIZE_MAX);
networkCacheEdit.setSingleStep(1);
networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB());
networkCacheEdit.setSuffix(" MB");
networkRedirectCacheTtlEdit.setMinimum(NETWORK_REDIRECT_CACHE_TTL_MIN);
networkRedirectCacheTtlEdit.setMaximum(NETWORK_REDIRECT_CACHE_TTL_MAX);
networkRedirectCacheTtlEdit.setSingleStep(1);
networkRedirectCacheTtlEdit.setValue(SettingsCache::instance().getRedirectCacheTtl());
auto networkCacheLayout = new QHBoxLayout;
networkCacheLayout->addStretch();
networkCacheLayout->addWidget(&networkCacheLabel);
networkCacheLayout->addWidget(&networkCacheEdit);
auto networkRedirectCacheLayout = new QHBoxLayout;
networkRedirectCacheLayout->addStretch();
networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlLabel);
networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlEdit);
auto pixmapCacheLayout = new QHBoxLayout;
pixmapCacheLayout->addStretch();
pixmapCacheLayout->addWidget(&pixmapCacheLabel);
pixmapCacheLayout->addWidget(&pixmapCacheEdit);
// Top Layout
lpGeneralGrid->addWidget(&picDownloadCheckBox, 0, 0);
lpGeneralGrid->addWidget(&resetDownloadURLs, 0, 1);
lpGeneralGrid->addLayout(urlListLayout, 1, 0, 1, 2);
lpGeneralGrid->addLayout(networkCacheLayout, 2, 1);
lpGeneralGrid->addLayout(networkRedirectCacheLayout, 3, 0);
lpGeneralGrid->addLayout(pixmapCacheLayout, 3, 1);
lpGeneralGrid->addWidget(&urlLinkLabel, 5, 0);
lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 5, 1);
lpGeneralGrid->addWidget(&urlLinkLabel, 4, 0);
// Spoiler Layout
lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0);
@ -1123,12 +1083,6 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, &SettingsCache::instance(),
&SettingsCache::setDownloadSpoilerStatus);
connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, this, &DeckEditorSettingsPage::setSpoilersEnabled);
connect(&pixmapCacheEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setPixmapCacheSize);
connect(&networkCacheEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setNetworkCacheSizeInMB);
connect(&networkRedirectCacheTtlEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setNetworkRedirectCacheTtl);
mpGeneralGroupBox = new QGroupBox;
mpGeneralGroupBox->setLayout(lpGeneralGrid);
@ -1154,46 +1108,6 @@ void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked()
QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset."));
}
void DeckEditorSettingsPage::clearDownloadedPicsButtonClicked()
{
CardPictureLoader::clearNetworkCache();
// These are not used anymore, but we don't delete them automatically, so
// we should do it here lest we leave pictures hanging around on users'
// machines.
QString picsPath = SettingsCache::instance().getPicsPath() + "/downloadedPics/";
QStringList dirs = QDir(picsPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
bool outerSuccessRemove = true;
for (const auto &dir : dirs) {
QString currentPath = picsPath + dir + "/";
QStringList files = QDir(currentPath).entryList(QDir::Files);
bool innerSuccessRemove = true;
for (int j = 0; j < files.length(); j++) {
if (!QDir(currentPath).remove(files.at(j))) {
qInfo() << "Failed to remove " + currentPath.toUtf8() + files.at(j).toUtf8();
outerSuccessRemove = false;
innerSuccessRemove = false;
}
qInfo() << "Removed " << currentPath << files.at(j);
}
if (innerSuccessRemove) {
bool success = QDir(picsPath).rmdir(dir);
if (!success) {
qInfo() << "Failed to remove inner directory" << picsPath;
} else {
qInfo() << "Removed" << currentPath;
}
}
}
if (outerSuccessRemove) {
QMessageBox::information(this, tr("Success"), tr("Downloaded card pictures have been reset."));
QDir(SettingsCache::instance().getPicsPath()).rmdir("downloadedPics");
} else {
QMessageBox::critical(this, tr("Error"), tr("One or more downloaded card pictures could not be cleared."));
}
}
void DeckEditorSettingsPage::actAddURL()
{
bool ok;
@ -1312,18 +1226,246 @@ void DeckEditorSettingsPage::retranslateUi()
tr("Do not close settings until manual update is complete"));
picDownloadCheckBox.setText(tr("Download card pictures on the fly"));
urlLinkLabel.setText(QString("<a href='%1'>%2</a>").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL")));
clearDownloadedPicsButton.setText(tr("Delete Downloaded Images"));
resetDownloadURLs.setText(tr("Reset Download URLs"));
updateNowButton->setText(tr("Update Spoilers"));
aAdd->setText(tr("Add New URL"));
aEdit->setText(tr("Edit URL"));
aRemove->setText(tr("Remove URL"));
}
StorageSettingsPage::StorageSettingsPage()
{
auto *lpNetworkCacheGrid = new QGridLayout;
auto *lpImageBackupGrid = new QGridLayout;
auto *lpPixmapCacheGrid = new QGridLayout;
networkCacheExplainerLabel.setWordWrap(true);
imageBackupExplainerLabel.setWordWrap(true);
pixmapCacheExplainerLabel.setWordWrap(true);
connect(&clearDownloadedPicsButton, &QPushButton::clicked, this,
&StorageSettingsPage::clearDownloadedPicsButtonClicked);
connect(&clearPixmapCacheButton, &QPushButton::clicked, this, &StorageSettingsPage::clearPixmapCacheButtonClicked);
// pixmap cache
pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN);
// 2047 is the max value to avoid overflowing of QPixmapCache::setCacheLimit(int size)
pixmapCacheEdit.setMaximum(PIXMAPCACHE_SIZE_MAX);
pixmapCacheEdit.setSingleStep(64);
pixmapCacheEdit.setValue(SettingsCache::instance().getPixmapCacheSize());
pixmapCacheEdit.setSuffix(" MB");
// Caching method
cardPictureLoaderCacheMethodComboBox = new QComboBox;
for (auto method : CardPictureLoaderCacheMethod::methods()) {
cardPictureLoaderCacheMethodComboBox->addItem(method.displayName, static_cast<int>(method.id));
}
int currentCacheMethod = static_cast<int>(SettingsCache::instance().getCardPictureLoaderCacheMethod());
int currentIndex = cardPictureLoaderCacheMethodComboBox->findData(currentCacheMethod);
if (currentIndex >= 0) {
cardPictureLoaderCacheMethodComboBox->setCurrentIndex(currentIndex);
}
connect(cardPictureLoaderCacheMethodComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this](int index) {
auto cacheMethod = static_cast<CardPictureLoaderCacheMethod::CacheMethod>(
cardPictureLoaderCacheMethodComboBox->itemData(index).toInt());
bool useNetworkCache = (cacheMethod == CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE);
if (useNetworkCache) {
clearImageBackupsButtonClicked();
} else {
clearDownloadedPicsButtonClicked();
}
mpNetworkCacheGroupBox->setEnabled(useNetworkCache);
mpImageBackupGroupBox->setEnabled(!useNetworkCache);
SettingsCache::instance().setCardImageCacheMethod(cacheMethod);
});
// Network Cache
networkCacheEdit.setMinimum(NETWORK_CACHE_SIZE_MIN);
networkCacheEdit.setMaximum(NETWORK_CACHE_SIZE_MAX);
networkCacheEdit.setSingleStep(1);
networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB());
networkCacheEdit.setSuffix(" MB");
networkRedirectCacheTtlEdit.setMinimum(NETWORK_REDIRECT_CACHE_TTL_MIN);
networkRedirectCacheTtlEdit.setMaximum(NETWORK_REDIRECT_CACHE_TTL_MAX);
networkRedirectCacheTtlEdit.setSingleStep(1);
networkRedirectCacheTtlEdit.setValue(SettingsCache::instance().getRedirectCacheTtl());
// Image Backup
localCardImageStorageNamingSchemeComboBox = new QComboBox;
for (const auto &scheme : CardPictureLoaderLocalSchemes::exportSchemes()) {
localCardImageStorageNamingSchemeComboBox->addItem(scheme.displayName, static_cast<int>(scheme.id));
}
int current = static_cast<int>(SettingsCache::instance().getLocalCardImageStorageNamingScheme());
int index = localCardImageStorageNamingSchemeComboBox->findData(current);
if (index >= 0) {
localCardImageStorageNamingSchemeComboBox->setCurrentIndex(index);
}
connect(localCardImageStorageNamingSchemeComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this](int index) {
auto scheme = static_cast<CardPictureLoaderLocalSchemes::NamingScheme>(
localCardImageStorageNamingSchemeComboBox->itemData(index).toInt());
SettingsCache::instance().setLocalCardImageStorageNamingScheme(scheme);
});
connect(&clearBackupsButton, &QPushButton::clicked, this, &StorageSettingsPage::clearImageBackupsButtonClicked);
auto cacheMethodLayout = new QHBoxLayout;
cacheMethodLayout->addWidget(&cardPictureLoaderCacheMethodLabel);
cacheMethodLayout->addWidget(cardPictureLoaderCacheMethodComboBox);
auto networkCacheLayout = new QHBoxLayout;
networkCacheLayout->addWidget(&clearDownloadedPicsButton);
networkCacheLayout->addStretch();
networkCacheLayout->addWidget(&networkCacheLabel);
networkCacheLayout->addWidget(&networkCacheEdit);
auto networkRedirectCacheLayout = new QHBoxLayout;
networkRedirectCacheLayout->addStretch();
networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlLabel);
networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlEdit);
auto pixmapCacheLayout = new QHBoxLayout;
pixmapCacheLayout->addWidget(&clearPixmapCacheButton);
pixmapCacheLayout->addStretch();
pixmapCacheLayout->addWidget(&pixmapCacheLabel);
pixmapCacheLayout->addWidget(&pixmapCacheEdit);
lpNetworkCacheGrid->addWidget(&networkCacheExplainerLabel, 0, 0);
lpNetworkCacheGrid->addLayout(networkCacheLayout, 1, 0);
lpNetworkCacheGrid->addLayout(networkRedirectCacheLayout, 2, 0);
// Image Backup Layout
lpImageBackupGrid->addWidget(&imageBackupExplainerLabel, 0, 0, 1, 2);
lpImageBackupGrid->addWidget(&localCardImageStorageNamingSchemeLabel, 1, 0);
lpImageBackupGrid->addWidget(localCardImageStorageNamingSchemeComboBox, 1, 1);
lpImageBackupGrid->addWidget(&clearBackupsButton, 2, 0);
lpPixmapCacheGrid->addWidget(&pixmapCacheExplainerLabel, 0, 0);
lpPixmapCacheGrid->addLayout(pixmapCacheLayout, 1, 0);
connect(&pixmapCacheEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setPixmapCacheSize);
connect(&networkCacheEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setNetworkCacheSizeInMB);
connect(&networkRedirectCacheTtlEdit, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setNetworkRedirectCacheTtl);
mpCacheMethodGroupBox = new QGroupBox;
mpCacheMethodGroupBox->setLayout(cacheMethodLayout);
mpNetworkCacheGroupBox = new QGroupBox;
mpNetworkCacheGroupBox->setLayout(lpNetworkCacheGrid);
mpImageBackupGroupBox = new QGroupBox;
mpImageBackupGroupBox->setLayout(lpImageBackupGrid);
mpPixmapCacheGroupBox = new QGroupBox;
mpPixmapCacheGroupBox->setLayout(lpPixmapCacheGrid);
auto *lpMainLayout = new QVBoxLayout;
lpMainLayout->addWidget(mpCacheMethodGroupBox);
lpMainLayout->addWidget(mpNetworkCacheGroupBox);
lpMainLayout->addWidget(mpImageBackupGroupBox);
lpMainLayout->addWidget(mpPixmapCacheGroupBox);
lpMainLayout->addStretch();
setLayout(lpMainLayout);
bool useNetworkCache = SettingsCache::instance().getCardPictureLoaderCacheMethod() ==
CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE;
mpNetworkCacheGroupBox->setEnabled(useNetworkCache);
mpImageBackupGroupBox->setEnabled(!useNetworkCache);
connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &StorageSettingsPage::retranslateUi);
retranslateUi();
}
void StorageSettingsPage::clearDownloadedPicsButtonClicked()
{
CardPictureLoader::clearNetworkCache();
CardPictureLoader::clearPixmapCache();
QMessageBox::information(this, tr("Success"), tr("Cached card pictures have been reset."));
}
void StorageSettingsPage::clearImageBackupsButtonClicked()
{
QString picsPath = SettingsCache::instance().getPicsPath() + "/downloadedPics";
QDir dir(picsPath);
bool success = dir.removeRecursively();
CardPictureLoader::clearPixmapCache();
if (success) {
QMessageBox::information(this, tr("Success"), tr("Downloaded card pictures have been reset."));
} else {
QMessageBox::critical(this, tr("Error"), tr("One or more downloaded card pictures could not be cleared."));
}
}
void StorageSettingsPage::clearPixmapCacheButtonClicked()
{
CardPictureLoader::clearPixmapCache();
QMessageBox::information(this, tr("Success"), tr("In-memory (currently loaded) card pictures have been reset."));
}
void StorageSettingsPage::retranslateUi()
{
cardPictureLoaderCacheMethodLabel.setText(tr("Card Picture Loader Caching Method:"));
networkCacheExplainerLabel.setText(
tr("The network cache is the preferred way of storing images. Downloaded images "
"are stored here until the size of the cache exceeds the configured size. Cockatrice automatically monitors "
"this cache and deletes the least recently seen card images to ensure the cache does not exceed the "
"configured size."));
imageBackupExplainerLabel.setText(
tr("Writing card images directly to a folder on your hard drive is another way "
"of storing images. This does not change how Cockatrice accesses or downloads "
"images. Cockatrice will NOT automatically monitor and clear this folder, so if you enable this option, it "
"is up to you to ensure sufficient available space. It should also be noted that if a provider outage "
"causes you to download the wrong picture (i.e. wrong printing) you will be stuck with it until you "
"manually delete the file, as opposed to using the network cache, which automatically rotates and thus "
"correct errors after a while."));
pixmapCacheExplainerLabel.setText(
tr("This is the in-memory picture cache used by the application at runtime. It determines how much memory "
"(RAM) Cockatrice can use before it has to fetch card images from the hard disk again. Increasing this will "
"allow more card images to be displayed at once but shouldn't be necessary. Clearing this will make "
"Cockatrice reload all images from the network cache or the disk."));
clearDownloadedPicsButton.setText(tr("Delete Cached Images"));
clearBackupsButton.setText(tr("Delete Saved Images"));
clearPixmapCacheButton.setText(tr("Clear In-Memory Images"));
mpCacheMethodGroupBox->setTitle(tr("Card Picture Loader Cache Method"));
mpNetworkCacheGroupBox->setTitle(tr("Network Cache"));
mpImageBackupGroupBox->setTitle(tr("Filesystem"));
mpPixmapCacheGroupBox->setTitle(tr("In-Memory Picture Cache"));
networkCacheLabel.setText(tr("Network Cache Size:"));
networkCacheEdit.setToolTip(tr("On-disk cache for downloaded pictures"));
networkRedirectCacheTtlLabel.setText(tr("Redirect Cache TTL:"));
networkRedirectCacheTtlEdit.setToolTip(tr("How long cached redirects for urls are valid for."));
pixmapCacheLabel.setText(tr("Picture Cache Size:"));
pixmapCacheEdit.setToolTip(tr("In-memory cache for pictures not currently on screen"));
updateNowButton->setText(tr("Update Spoilers"));
aAdd->setText(tr("Add New URL"));
aEdit->setText(tr("Edit URL"));
aRemove->setText(tr("Remove URL"));
localCardImageStorageNamingSchemeLabel.setText(tr("Naming scheme:"));
networkRedirectCacheTtlEdit.setSuffix(" " + tr("Day(s)"));
}
@ -1803,6 +1945,7 @@ DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent)
pagesWidget->addWidget(makeScrollable(new AppearanceSettingsPage));
pagesWidget->addWidget(makeScrollable(new UserInterfaceSettingsPage));
pagesWidget->addWidget(new DeckEditorSettingsPage);
pagesWidget->addWidget(makeScrollable(new StorageSettingsPage));
pagesWidget->addWidget(new MessagesSettingsPage);
pagesWidget->addWidget(new SoundSettingsPage);
pagesWidget->addWidget(new ShortcutSettingsPage);
@ -1851,6 +1994,11 @@ void DlgSettings::createIcons()
deckEditorButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
deckEditorButton->setIcon(QPixmap("theme:config/deckeditor"));
storageButton = new QListWidgetItem(contentsWidget);
storageButton->setTextAlignment(Qt::AlignHCenter);
storageButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
storageButton->setIcon(QPixmap("theme:config/storage"));
messagesButton = new QListWidgetItem(contentsWidget);
messagesButton->setTextAlignment(Qt::AlignHCenter);
messagesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
@ -1975,6 +2123,7 @@ void DlgSettings::retranslateUi()
generalButton->setText(tr("General"));
appearanceButton->setText(tr("Appearance"));
userInterfaceButton->setText(tr("User Interface"));
storageButton->setText(tr("Storage"));
deckEditorButton->setText(tr("Card Sources"));
messagesButton->setText(tr("Chat"));
soundButton->setText(tr("Sound"));

View file

@ -213,11 +213,9 @@ private slots:
void actAddURL();
void actRemoveURL();
void actEditURL();
void clearDownloadedPicsButtonClicked();
void resetDownloadedURLsButtonClicked();
private:
QPushButton clearDownloadedPicsButton;
QPushButton resetDownloadURLs;
QLabel urlLinkLabel;
QCheckBox picDownloadCheckBox;
@ -227,18 +225,51 @@ private:
QLabel msDownloadSpoilersLabel;
QGroupBox *mpGeneralGroupBox;
QGroupBox *mpSpoilerGroupBox;
QLineEdit *mpSpoilerSavePathLineEdit;
QLabel mcSpoilerSaveLabel;
QLabel lastUpdatedLabel;
QLabel infoOnSpoilersLabel;
QPushButton *mpSpoilerPathButton;
QPushButton *updateNowButton;
};
class StorageSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
StorageSettingsPage();
void retranslateUi() override;
private slots:
void clearDownloadedPicsButtonClicked();
void clearImageBackupsButtonClicked();
void clearPixmapCacheButtonClicked();
private:
QPushButton clearDownloadedPicsButton;
QPushButton clearBackupsButton;
QPushButton clearPixmapCacheButton;
QGroupBox *mpCacheMethodGroupBox;
QGroupBox *mpNetworkCacheGroupBox;
QGroupBox *mpImageBackupGroupBox;
QGroupBox *mpPixmapCacheGroupBox;
QLabel networkCacheExplainerLabel;
QLabel imageBackupExplainerLabel;
QLabel pixmapCacheExplainerLabel;
QLabel cardPictureLoaderCacheMethodLabel;
QComboBox *cardPictureLoaderCacheMethodComboBox;
QLabel networkCacheLabel;
QSpinBox networkCacheEdit;
QLabel networkRedirectCacheTtlLabel;
QSpinBox networkRedirectCacheTtlEdit;
QSpinBox pixmapCacheEdit;
QLabel pixmapCacheLabel;
QLabel localCardImageStorageNamingSchemeLabel;
QComboBox *localCardImageStorageNamingSchemeComboBox;
};
class MessagesSettingsPage : public AbstractSettingsPage
@ -353,8 +384,8 @@ private slots:
private:
QListWidget *contentsWidget;
QStackedWidget *pagesWidget;
QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton,
*soundButton, *shortcutsButton;
QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *storageButton,
*messagesButton, *soundButton, *shortcutsButton;
void createIcons();
void retranslateUi();