Tip of the Day (#3118)
* Basic tip of the day with sample widget added * "Show tips on startup" option added to settings * tip cycling implemented * Structure of the tipOfTheDay class and resource created * tip getter function modified * Resources added, feature works properly * clangified * accidental modification rolled back * zach cleanup * tips to spaces; cmake list combined * cleanup img * fix copy * remove TOTD as QObject so we can copy construct it * prevent mem leaks in dlg * changed order of 'next' and 'previous' buttons * Date and tip numbers added; content wraps around * useless sizepolicy removed * link support added & clangified * Initial tips & memory management updates
|
@ -19,6 +19,8 @@ SET(cockatrice_SOURCES
|
||||||
src/dlg_forgotpasswordreset.cpp
|
src/dlg_forgotpasswordreset.cpp
|
||||||
src/dlg_forgotpasswordchallenge.cpp
|
src/dlg_forgotpasswordchallenge.cpp
|
||||||
src/dlg_register.cpp
|
src/dlg_register.cpp
|
||||||
|
src/dlg_tip_of_the_day.cpp
|
||||||
|
src/tip_of_the_day.cpp
|
||||||
src/dlg_update.cpp
|
src/dlg_update.cpp
|
||||||
src/dlg_viewlog.cpp
|
src/dlg_viewlog.cpp
|
||||||
src/abstractclient.cpp
|
src/abstractclient.cpp
|
||||||
|
|
|
@ -342,5 +342,19 @@
|
||||||
<file>resources/userlevels/admin_vip.svg</file>
|
<file>resources/userlevels/admin_vip.svg</file>
|
||||||
<file>resources/userlevels/admin_vip_buddy.svg</file>
|
<file>resources/userlevels/admin_vip_buddy.svg</file>
|
||||||
|
|
||||||
|
<!-- ADD TIP OF THE DAY IMAGES HERE -->
|
||||||
|
<file>resources/tips/tips_of_the_day.xml</file>
|
||||||
|
<file>resources/tips/images/accounts_tab.png</file>
|
||||||
|
<file>resources/tips/images/arrows.png</file>
|
||||||
|
<file>resources/tips/images/cockatrice_register.png</file>
|
||||||
|
<file>resources/tips/images/cockatrice_wiki.png</file>
|
||||||
|
<file>resources/tips/images/coin_flip.png</file>
|
||||||
|
<file>resources/tips/images/face_down.png</file>
|
||||||
|
<file>resources/tips/images/filter_games.png</file>
|
||||||
|
<file>resources/tips/images/github_logo.png</file>
|
||||||
|
<file>resources/tips/images/gitter.png</file>
|
||||||
|
<file>resources/tips/images/themes.png</file>
|
||||||
|
<file>resources/tips/images/tip_of_the_day.png</file>
|
||||||
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
BIN
cockatrice/resources/tips/images/accounts_tab.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
cockatrice/resources/tips/images/arrows.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
cockatrice/resources/tips/images/cockatrice_register.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
cockatrice/resources/tips/images/cockatrice_wiki.png
Normal file
After Width: | Height: | Size: 167 KiB |
BIN
cockatrice/resources/tips/images/coin_flip.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
cockatrice/resources/tips/images/face_down.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
cockatrice/resources/tips/images/filter_games.png
Normal file
After Width: | Height: | Size: 227 KiB |
BIN
cockatrice/resources/tips/images/github_logo.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
cockatrice/resources/tips/images/gitter.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
cockatrice/resources/tips/images/themes.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
cockatrice/resources/tips/images/tip_of_the_day.png
Normal file
After Width: | Height: | Size: 92 KiB |
86
cockatrice/resources/tips/tips_of_the_day.xml
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<tips>
|
||||||
|
<tip>
|
||||||
|
<title>Tip of the Day</title>
|
||||||
|
<text>Tip of the Day is a new feature to Cockatrice that allows users to get information about the newest features of the program and some of the most commonly asked questions!</text>
|
||||||
|
<image>tip_of_the_day.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Suggesting New Tips</title>
|
||||||
|
<text>You can suggest new Tips of the Day by reaching out to the development team on <a href="https://gitter.im/cockatrice/cockatrice">Gitter</a>!</text>
|
||||||
|
<image>gitter.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Reporting Bugs</title>
|
||||||
|
<text>If you encounter a bug while using Cockatrice, you can report the bug to the development team via <a href="https://github.com/cockatrice/cockatrice/issues">GitHub<a></text>
|
||||||
|
<image>github_logo.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>FAQ/Troubleshooting Wiki</title>
|
||||||
|
<text>You can find answers to the most common questions and some helpful Cockatrice toubleshooting over on the <a href="https://github.com/cockatrice/cockatrice/wiki">GitHub wiki<a></text>
|
||||||
|
<image>cockatrice_wiki.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Register for a Server</title>
|
||||||
|
<text>Click on either Cockatrice (Windows) or Actions (Mac) and then Register to server... When the dialogue appears, fill out the desired server information.</text>
|
||||||
|
<image>cockatrice_register.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Drawing Arrows</title>
|
||||||
|
<text>You can draw arrows of different color by holding a combination of keys!
|
||||||
|
Right Click: Red Arrow
|
||||||
|
Shift + Right Click: Green Arrow
|
||||||
|
Alt + Right Click: Blue Arrow
|
||||||
|
Cmd + Right Click: Yellow Arrow
|
||||||
|
</text>
|
||||||
|
<image>arrows.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Filtering Games</title>
|
||||||
|
<text>Don't see all the active games? Want to see a smaller selection? Use the Game Filters to change your horizon</text>
|
||||||
|
<image>filter_games.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Upload Custom Avatar</title>
|
||||||
|
<text>Want to show off your hippo avatar? Need to update your password? Check out the Accounts Tab for more info!</text>
|
||||||
|
<image>accounts_tab.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Common Shortcuts</title>
|
||||||
|
<text>You can find a full list of shortcuts <a href="https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts">on the wiki</a>, but a short list:
|
||||||
|
<br>Roll a die: CTRL + I
|
||||||
|
<br>Mulligan: CTRL + M
|
||||||
|
<br>Draw a card: CTRL + D
|
||||||
|
<br>Undo a draw: CTRL + SHIFT + D
|
||||||
|
<br>View Sideboard: CTRL + F3
|
||||||
|
<br>Change Life: CTRL + L
|
||||||
|
<br>All shortcuts can be customized via Settings->Shortcuts!
|
||||||
|
</text>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Changing Themes</title>
|
||||||
|
<text>Did you know Cockatrice has custom themes? You can either <a href="https://github.com/Cockatrice/Cockatrice/wiki/Themes">create one yourself</a> or use one of the several pre-loaded ones! Go to Settings->Appearance and try them out!</text>
|
||||||
|
<image>themes.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Flip of the Coin</title>
|
||||||
|
<text>You can flip a coin instead of rolling a die by rolling a 2 sided die instead!</text>
|
||||||
|
<image>coin_flip.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
<tip>
|
||||||
|
<title>Face Down Cards</title>
|
||||||
|
<text>You can hold Shift while dragging or clicking on a card to have it enter play face down</text>
|
||||||
|
<image>face_down.png</image>
|
||||||
|
<date>2018-03-01</date>
|
||||||
|
</tip>
|
||||||
|
</tips>
|
|
@ -67,6 +67,8 @@ GeneralSettingsPage::GeneralSettingsPage()
|
||||||
defaultUrlEdit = new QLineEdit(settingsCache->getPicUrl());
|
defaultUrlEdit = new QLineEdit(settingsCache->getPicUrl());
|
||||||
fallbackUrlEdit = new QLineEdit(settingsCache->getPicUrlFallback());
|
fallbackUrlEdit = new QLineEdit(settingsCache->getPicUrlFallback());
|
||||||
|
|
||||||
|
showTipsOnStartup.setChecked(settingsCache->getShowTipsOnStartup());
|
||||||
|
|
||||||
connect(&clearDownloadedPicsButton, SIGNAL(clicked()), this, SLOT(clearDownloadedPicsButtonClicked()));
|
connect(&clearDownloadedPicsButton, SIGNAL(clicked()), this, SLOT(clearDownloadedPicsButtonClicked()));
|
||||||
connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
|
connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
|
||||||
connect(&picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int)));
|
connect(&picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int)));
|
||||||
|
@ -79,6 +81,7 @@ GeneralSettingsPage::GeneralSettingsPage()
|
||||||
connect(fallbackUrlEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlFallback(QString)));
|
connect(fallbackUrlEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlFallback(QString)));
|
||||||
connect(&defaultUrlRestoreButton, SIGNAL(clicked()), this, SLOT(defaultUrlRestoreButtonClicked()));
|
connect(&defaultUrlRestoreButton, SIGNAL(clicked()), this, SLOT(defaultUrlRestoreButtonClicked()));
|
||||||
connect(&fallbackUrlRestoreButton, SIGNAL(clicked()), this, SLOT(fallbackUrlRestoreButtonClicked()));
|
connect(&fallbackUrlRestoreButton, SIGNAL(clicked()), this, SLOT(fallbackUrlRestoreButtonClicked()));
|
||||||
|
connect(&showTipsOnStartup, SIGNAL(clicked(bool)), settingsCache, SLOT(setShowTipsOnStartup(bool)));
|
||||||
|
|
||||||
setEnabledStatus(settingsCache->getPicDownload());
|
setEnabledStatus(settingsCache->getPicDownload());
|
||||||
|
|
||||||
|
@ -97,6 +100,7 @@ GeneralSettingsPage::GeneralSettingsPage()
|
||||||
personalGrid->addWidget(&fallbackUrlLabel, 6, 0, 1, 1);
|
personalGrid->addWidget(&fallbackUrlLabel, 6, 0, 1, 1);
|
||||||
personalGrid->addWidget(fallbackUrlEdit, 6, 1, 1, 1);
|
personalGrid->addWidget(fallbackUrlEdit, 6, 1, 1, 1);
|
||||||
personalGrid->addWidget(&fallbackUrlRestoreButton, 6, 2, 1, 1);
|
personalGrid->addWidget(&fallbackUrlRestoreButton, 6, 2, 1, 1);
|
||||||
|
personalGrid->addWidget(&showTipsOnStartup, 7, 0);
|
||||||
personalGrid->addWidget(&urlLinkLabel, 7, 1, 1, 1);
|
personalGrid->addWidget(&urlLinkLabel, 7, 1, 1, 1);
|
||||||
personalGrid->addWidget(&clearDownloadedPicsButton, 8, 0, 1, 3);
|
personalGrid->addWidget(&clearDownloadedPicsButton, 8, 0, 1, 3);
|
||||||
|
|
||||||
|
@ -310,6 +314,7 @@ void GeneralSettingsPage::retranslateUi()
|
||||||
updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client"));
|
updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client"));
|
||||||
defaultUrlRestoreButton.setText(tr("Reset"));
|
defaultUrlRestoreButton.setText(tr("Reset"));
|
||||||
fallbackUrlRestoreButton.setText(tr("Reset"));
|
fallbackUrlRestoreButton.setText(tr("Reset"));
|
||||||
|
showTipsOnStartup.setText(tr("Show tips on startup"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettingsPage::setEnabledStatus(bool status)
|
void GeneralSettingsPage::setEnabledStatus(bool status)
|
||||||
|
|
|
@ -82,6 +82,7 @@ private:
|
||||||
QPushButton clearDownloadedPicsButton;
|
QPushButton clearDownloadedPicsButton;
|
||||||
QPushButton defaultUrlRestoreButton;
|
QPushButton defaultUrlRestoreButton;
|
||||||
QPushButton fallbackUrlRestoreButton;
|
QPushButton fallbackUrlRestoreButton;
|
||||||
|
QCheckBox showTipsOnStartup;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AppearanceSettingsPage : public AbstractSettingsPage
|
class AppearanceSettingsPage : public AbstractSettingsPage
|
||||||
|
|
153
cockatrice/src/dlg_tip_of_the_day.cpp
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QDate>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
#include "dlg_tip_of_the_day.h"
|
||||||
|
#include "settingscache.h"
|
||||||
|
#include "tip_of_the_day.h"
|
||||||
|
|
||||||
|
#define MIN_TIP_IMAGE_HEIGHT 200
|
||||||
|
#define MIN_TIP_IMAGE_WIDTH 200
|
||||||
|
#define MAX_TIP_IMAGE_HEIGHT 300
|
||||||
|
#define MAX_TIP_IMAGE_WIDTH 300
|
||||||
|
|
||||||
|
DlgTipOfTheDay::DlgTipOfTheDay(QWidget *parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
successfulInit = false;
|
||||||
|
QString xmlPath = "theme:tips/tips_of_the_day.xml";
|
||||||
|
tipDatabase = new TipsOfTheDay(xmlPath, this);
|
||||||
|
|
||||||
|
if (tipDatabase->rowCount() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
title = new QLabel();
|
||||||
|
title->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
tipTextContent = new QLabel();
|
||||||
|
tipTextContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
tipTextContent->setWordWrap(true);
|
||||||
|
tipTextContent->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||||
|
tipTextContent->setOpenExternalLinks(true);
|
||||||
|
imageLabel = new QLabel();
|
||||||
|
imageLabel->setFixedHeight(MAX_TIP_IMAGE_HEIGHT + 50);
|
||||||
|
imageLabel->setFixedWidth(MAX_TIP_IMAGE_WIDTH + 50);
|
||||||
|
image = new QPixmap();
|
||||||
|
date = new QLabel();
|
||||||
|
date->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
tipNumber = new QLabel();
|
||||||
|
tipNumber->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
currentTip = settingsCache->getLastShownTip() + 1;
|
||||||
|
connect(this, SIGNAL(newTipRequested(int)), this, SLOT(updateTip(int)));
|
||||||
|
newTipRequested(currentTip);
|
||||||
|
|
||||||
|
content = new QVBoxLayout;
|
||||||
|
content->addWidget(title);
|
||||||
|
content->addWidget(tipTextContent);
|
||||||
|
content->addWidget(imageLabel);
|
||||||
|
content->addWidget(date);
|
||||||
|
|
||||||
|
buttonBox = new QDialogButtonBox(Qt::Horizontal);
|
||||||
|
nextButton = new QPushButton(tr("Next"));
|
||||||
|
previousButton = new QPushButton(tr("Previous"));
|
||||||
|
buttonBox->addButton(previousButton, QDialogButtonBox::ActionRole);
|
||||||
|
buttonBox->addButton(nextButton, QDialogButtonBox::ActionRole);
|
||||||
|
buttonBox->addButton(QDialogButtonBox::Ok);
|
||||||
|
buttonBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
|
connect(nextButton, SIGNAL(clicked()), this, SLOT(nextClicked()));
|
||||||
|
connect(previousButton, SIGNAL(clicked()), this, SLOT(previousClicked()));
|
||||||
|
|
||||||
|
showTipsOnStartupCheck = new QCheckBox("Show tips on startup");
|
||||||
|
showTipsOnStartupCheck->setChecked(true);
|
||||||
|
connect(showTipsOnStartupCheck, SIGNAL(clicked(bool)), settingsCache, SLOT(setShowTipsOnStartup(bool)));
|
||||||
|
buttonBar = new QHBoxLayout();
|
||||||
|
buttonBar->addWidget(showTipsOnStartupCheck);
|
||||||
|
buttonBar->addWidget(tipNumber);
|
||||||
|
buttonBar->addWidget(buttonBox);
|
||||||
|
|
||||||
|
mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->addLayout(content);
|
||||||
|
mainLayout->addLayout(buttonBar);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
setWindowTitle(tr("Tip of the Day"));
|
||||||
|
setMinimumWidth(500);
|
||||||
|
setMinimumHeight(300);
|
||||||
|
successfulInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DlgTipOfTheDay::~DlgTipOfTheDay()
|
||||||
|
{
|
||||||
|
tipDatabase->deleteLater();
|
||||||
|
title->deleteLater();
|
||||||
|
tipTextContent->deleteLater();
|
||||||
|
imageLabel->deleteLater();
|
||||||
|
tipNumber->deleteLater();
|
||||||
|
showTipsOnStartupCheck->deleteLater();
|
||||||
|
content->deleteLater();
|
||||||
|
mainLayout->deleteLater();
|
||||||
|
buttonBox->deleteLater();
|
||||||
|
nextButton->deleteLater();
|
||||||
|
previousButton->deleteLater();
|
||||||
|
buttonBar->deleteLater();
|
||||||
|
delete image;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DlgTipOfTheDay::nextClicked()
|
||||||
|
{
|
||||||
|
emit newTipRequested(currentTip + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DlgTipOfTheDay::previousClicked()
|
||||||
|
{
|
||||||
|
emit newTipRequested(currentTip - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DlgTipOfTheDay::updateTip(int tipId)
|
||||||
|
{
|
||||||
|
QString titleText, contentText, imagePath;
|
||||||
|
|
||||||
|
if (tipId < 0) {
|
||||||
|
tipId = tipDatabase->rowCount() - 1;
|
||||||
|
} else if (tipId >= tipDatabase->rowCount()) {
|
||||||
|
tipId = tipId % tipDatabase->rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
TipOfTheDay tip = tipDatabase->getTip(tipId);
|
||||||
|
titleText = tip.getTitle();
|
||||||
|
contentText = tip.getContent();
|
||||||
|
imagePath = tip.getImagePath();
|
||||||
|
|
||||||
|
title->setText("<h2>" + titleText + "</h2>");
|
||||||
|
tipTextContent->setText(contentText);
|
||||||
|
|
||||||
|
if (!image->load(imagePath)) {
|
||||||
|
qDebug() << "Image failed to load from" << imagePath;
|
||||||
|
imageLabel->clear();
|
||||||
|
} else {
|
||||||
|
int h = std::min(std::max(image->height(), MIN_TIP_IMAGE_HEIGHT), MAX_TIP_IMAGE_HEIGHT);
|
||||||
|
int w = std::min(std::max(imageLabel->width(), MIN_TIP_IMAGE_WIDTH), MAX_TIP_IMAGE_WIDTH);
|
||||||
|
imageLabel->setPixmap(image->scaled(h, w, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
|
}
|
||||||
|
|
||||||
|
date->setText("<i>Tip added on: " + tip.getDate().toString("yyyy.MM.dd") + "</i>");
|
||||||
|
|
||||||
|
tipNumber->setText("Tip " + QString::number(tipId + 1) + " / " + QString::number(tipDatabase->rowCount()));
|
||||||
|
|
||||||
|
currentTip = static_cast<unsigned int>(tipId);
|
||||||
|
settingsCache->setLastShownTip(currentTip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DlgTipOfTheDay::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
int h = imageLabel->height();
|
||||||
|
int w = imageLabel->width();
|
||||||
|
imageLabel->setPixmap(image->scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
|
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
}
|
48
cockatrice/src/dlg_tip_of_the_day.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef DLG_TIPOFDAY_H
|
||||||
|
#define DLG_TIPOFDAY_H
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QPushButton;
|
||||||
|
class QCheckBox;
|
||||||
|
class TipsOfTheDay;
|
||||||
|
|
||||||
|
class DlgTipOfTheDay : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DlgTipOfTheDay(QWidget *parent = nullptr);
|
||||||
|
~DlgTipOfTheDay() override;
|
||||||
|
bool successfulInit;
|
||||||
|
signals:
|
||||||
|
void newTipRequested(int tipId);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int currentTip;
|
||||||
|
TipsOfTheDay *tipDatabase;
|
||||||
|
QLabel *title, *tipTextContent, *imageLabel, *tipNumber, *date;
|
||||||
|
QCheckBox *showTipsOnStartupCheck;
|
||||||
|
QPixmap *image;
|
||||||
|
|
||||||
|
QVBoxLayout *content, *mainLayout;
|
||||||
|
QDialogButtonBox *buttonBox;
|
||||||
|
QPushButton *nextButton, *previousButton;
|
||||||
|
QHBoxLayout *buttonBar;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void nextClicked();
|
||||||
|
void previousClicked();
|
||||||
|
void updateTip(int tipId);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -22,6 +22,7 @@
|
||||||
#include "QtNetwork/QNetworkInterface"
|
#include "QtNetwork/QNetworkInterface"
|
||||||
#include "carddatabase.h"
|
#include "carddatabase.h"
|
||||||
#include "dlg_settings.h"
|
#include "dlg_settings.h"
|
||||||
|
#include "dlg_tip_of_the_day.h"
|
||||||
#include "featureset.h"
|
#include "featureset.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "pixmapgenerator.h"
|
#include "pixmapgenerator.h"
|
||||||
|
@ -137,6 +138,11 @@ int main(int argc, char *argv[])
|
||||||
ui.show();
|
ui.show();
|
||||||
qDebug("main(): ui.show() finished");
|
qDebug("main(): ui.show() finished");
|
||||||
|
|
||||||
|
DlgTipOfTheDay tip;
|
||||||
|
if (settingsCache->getShowTipsOnStartup() && tip.successfulInit) {
|
||||||
|
tip.show();
|
||||||
|
}
|
||||||
|
|
||||||
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
|
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
app.exec();
|
app.exec();
|
||||||
|
|
||||||
|
|
|
@ -220,8 +220,8 @@ void SetsModel::sort(int column, Qt::SortOrder order)
|
||||||
void SetsModel::save(CardDatabase *db)
|
void SetsModel::save(CardDatabase *db)
|
||||||
{
|
{
|
||||||
// order
|
// order
|
||||||
for (unsigned int i = 0; i < sets.size(); i++)
|
for (int i = 0; i < sets.size(); i++)
|
||||||
sets[i]->setSortKey(i + 1);
|
sets[i]->setSortKey(static_cast<unsigned int>(i + 1));
|
||||||
|
|
||||||
// enabled sets
|
// enabled sets
|
||||||
for (const CardSetPtr &set : sets)
|
for (const CardSetPtr &set : sets)
|
||||||
|
|
|
@ -179,6 +179,10 @@ SettingsCache::SettingsCache()
|
||||||
lang = settings->value("personal/lang").toString();
|
lang = settings->value("personal/lang").toString();
|
||||||
keepalive = settings->value("personal/keepalive", 5).toInt();
|
keepalive = settings->value("personal/keepalive", 5).toInt();
|
||||||
|
|
||||||
|
// tip of the day settings
|
||||||
|
showTipsOnStartup = settings->value("tipOfDay/showTips", true).toBool();
|
||||||
|
lastShownTip = settings->value("tipOfDay/lastShown", -1).toInt();
|
||||||
|
|
||||||
deckPath = getSafeConfigPath("paths/decks", dataPath + "/decks/");
|
deckPath = getSafeConfigPath("paths/decks", dataPath + "/decks/");
|
||||||
replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/");
|
replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/");
|
||||||
picsPath = getSafeConfigPath("paths/pics", dataPath + "/pics/");
|
picsPath = getSafeConfigPath("paths/pics", dataPath + "/pics/");
|
||||||
|
@ -335,6 +339,18 @@ void SettingsCache::setLang(const QString &_lang)
|
||||||
emit langChanged();
|
emit langChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setShowTipsOnStartup(bool _showTipsOnStartup)
|
||||||
|
{
|
||||||
|
showTipsOnStartup = _showTipsOnStartup;
|
||||||
|
settings->setValue("tipOfDay/showTips", showTipsOnStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setLastShownTip(int _lastShownTip)
|
||||||
|
{
|
||||||
|
lastShownTip = _lastShownTip;
|
||||||
|
settings->setValue("tipOfDay/lastShown", lastShownTip);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setDeckPath(const QString &_deckPath)
|
void SettingsCache::setDeckPath(const QString &_deckPath)
|
||||||
{
|
{
|
||||||
deckPath = _deckPath;
|
deckPath = _deckPath;
|
||||||
|
|
|
@ -67,6 +67,8 @@ private:
|
||||||
QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath,
|
QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath,
|
||||||
spoilerDatabasePath, tokenDatabasePath, themeName;
|
spoilerDatabasePath, tokenDatabasePath, themeName;
|
||||||
bool notifyAboutUpdates;
|
bool notifyAboutUpdates;
|
||||||
|
bool showTipsOnStartup;
|
||||||
|
unsigned int lastShownTip;
|
||||||
bool mbDownloadSpoilers;
|
bool mbDownloadSpoilers;
|
||||||
int updateReleaseChannel;
|
int updateReleaseChannel;
|
||||||
int maxFontSize;
|
int maxFontSize;
|
||||||
|
@ -199,6 +201,14 @@ public:
|
||||||
{
|
{
|
||||||
return notifyAboutUpdates;
|
return notifyAboutUpdates;
|
||||||
}
|
}
|
||||||
|
bool getShowTipsOnStartup() const
|
||||||
|
{
|
||||||
|
return showTipsOnStartup;
|
||||||
|
}
|
||||||
|
unsigned int getLastShownTip() const
|
||||||
|
{
|
||||||
|
return lastShownTip;
|
||||||
|
}
|
||||||
ReleaseChannel *getUpdateReleaseChannel() const
|
ReleaseChannel *getUpdateReleaseChannel() const
|
||||||
{
|
{
|
||||||
return releaseChannels.at(updateReleaseChannel);
|
return releaseChannels.at(updateReleaseChannel);
|
||||||
|
@ -433,6 +443,8 @@ public slots:
|
||||||
void setMainWindowGeometry(const QByteArray &_mainWindowGeometry);
|
void setMainWindowGeometry(const QByteArray &_mainWindowGeometry);
|
||||||
void setTokenDialogGeometry(const QByteArray &_tokenDialog);
|
void setTokenDialogGeometry(const QByteArray &_tokenDialog);
|
||||||
void setLang(const QString &_lang);
|
void setLang(const QString &_lang);
|
||||||
|
void setShowTipsOnStartup(bool _showTipsOnStartup);
|
||||||
|
void setLastShownTip(int _lastShowTip);
|
||||||
void setDeckPath(const QString &_deckPath);
|
void setDeckPath(const QString &_deckPath);
|
||||||
void setReplaysPath(const QString &_replaysPath);
|
void setReplaysPath(const QString &_replaysPath);
|
||||||
void setPicsPath(const QString &_picsPath);
|
void setPicsPath(const QString &_picsPath);
|
||||||
|
|
99
cockatrice/src/tip_of_the_day.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
#include <QDate>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "tip_of_the_day.h"
|
||||||
|
|
||||||
|
#define TIPDDBMODEL_COLUMNS 3
|
||||||
|
|
||||||
|
TipOfTheDay::TipOfTheDay(QString _title, QString _content, QString _imagePath, QDate _date)
|
||||||
|
: title(std::move(_title)), content(std::move(_content)), imagePath(std::move(_imagePath)), date(_date)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TipsOfTheDay::TipsOfTheDay(QString xmlPath, QObject *parent) : QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
tipList = new QList<TipOfTheDay>;
|
||||||
|
|
||||||
|
QFile xmlFile(xmlPath);
|
||||||
|
|
||||||
|
QTextStream errorStream(stderr);
|
||||||
|
if (!QFile::exists(xmlPath)) {
|
||||||
|
errorStream << tr("File does not exist.\n");
|
||||||
|
return;
|
||||||
|
} else if (!xmlFile.open(QIODevice::ReadOnly)) {
|
||||||
|
errorStream << tr("Failed to open file.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmlStreamReader reader(&xmlFile);
|
||||||
|
|
||||||
|
while (!reader.atEnd()) {
|
||||||
|
if (reader.readNext() == QXmlStreamReader::EndElement) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.name() == "tip") {
|
||||||
|
QString title, content, imagePath;
|
||||||
|
QDate date;
|
||||||
|
reader.readNext();
|
||||||
|
while (!reader.atEnd()) {
|
||||||
|
if (reader.readNext() == QXmlStreamReader::EndElement) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.name() == "title") {
|
||||||
|
title = reader.readElementText();
|
||||||
|
} else if (reader.name() == "text") {
|
||||||
|
content = reader.readElementText();
|
||||||
|
} else if (reader.name() == "image") {
|
||||||
|
imagePath = "theme:tips/images/" + reader.readElementText();
|
||||||
|
} else if (reader.name() == "date") {
|
||||||
|
date = QDate::fromString(reader.readElementText(), Qt::ISODate);
|
||||||
|
} else {
|
||||||
|
// unkown element, do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tipList->append(TipOfTheDay(title, content, imagePath, date));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TipsOfTheDay::~TipsOfTheDay()
|
||||||
|
{
|
||||||
|
delete tipList;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TipsOfTheDay::data(const QModelIndex &index, int /*role*/) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= tipList->size() || index.column() >= TIPDDBMODEL_COLUMNS)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
TipOfTheDay tip = tipList->at(index.row());
|
||||||
|
switch (index.column()) {
|
||||||
|
case TitleColumn:
|
||||||
|
return tip.getTitle();
|
||||||
|
case ContentColumn:
|
||||||
|
return tip.getContent();
|
||||||
|
case ImagePathColumn:
|
||||||
|
return tip.getImagePath();
|
||||||
|
case DateColumn:
|
||||||
|
return tip.getDate();
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TipOfTheDay TipsOfTheDay::getTip(int tipId)
|
||||||
|
{
|
||||||
|
return tipList->at(tipId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TipsOfTheDay::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return tipList->size();
|
||||||
|
}
|
54
cockatrice/src/tip_of_the_day.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef TIP_OF_DAY_H
|
||||||
|
#define TIP_OF_DAY_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
class TipOfTheDay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TipOfTheDay(QString _title, QString _content, QString _imagePath, QDate _date);
|
||||||
|
QString getTitle() const
|
||||||
|
{
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
QString getContent() const
|
||||||
|
{
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
QString getImagePath() const
|
||||||
|
{
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
QDate getDate() const
|
||||||
|
{
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString title, content, imagePath;
|
||||||
|
QDate date;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TipsOfTheDay : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Columns
|
||||||
|
{
|
||||||
|
TitleColumn,
|
||||||
|
ContentColumn,
|
||||||
|
ImagePathColumn,
|
||||||
|
DateColumn,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TipsOfTheDay(QString xmlPath, QObject *parent = nullptr);
|
||||||
|
~TipsOfTheDay() override;
|
||||||
|
TipOfTheDay getTip(int tipId);
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<TipOfTheDay> *tipList;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|