view src/gui/dialog/information.cc @ 118:39521c47c7a3

*: another huge megacommit, SORRY The torrents page works a lot better now Added the edit option to the anime list right click menu Vectorized currently playing files Available player and extensions are now loaded at runtime from files in (dotpath)/players.json and (dotpath)/extensions.json These paths are not permanent and will likely be moved to (dotpath)/recognition ... ... ...
author Paper <mrpapersonic@gmail.com>
date Tue, 07 Nov 2023 23:40:54 -0500
parents 2004b41d4a59
children 4d461ef7d424
line wrap: on
line source

#include "gui/dialog/information.h"
#include "core/anime.h"
#include "core/anime_db.h"
#include "core/strings.h"
#include "core/session.h"
#include "gui/pages/anime_list.h"
#include "gui/translate/anime.h"
#include "gui/widgets/anime_info.h"
#include "gui/widgets/optional_date.h"
#include "gui/widgets/poster.h"
#include "gui/widgets/text.h"
#include "gui/window.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDateEdit>
#include <QDebug>
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QSpinBox>
#include <QStringList>
#include <QTextStream>
#include <QVBoxLayout>
#include <functional>
#ifdef WIN32
#include "sys/win32/dark_theme.h"
#endif

/* TODO: Taiga disables rendering of the tab widget entirely when the anime is not part of a list,
   which sucks. Think of a better way to implement this later. */
void InformationDialog::SaveData(Anime::Anime& anime) {
	anime.SetUserProgress(_progress);
	anime.SetUserScore(_score);
	anime.SetUserIsRewatching(_rewatching);
	anime.SetUserStatus(_status);
	anime.SetUserNotes(_notes);
	anime.SetUserDateStarted(_started);
	anime.SetUserDateCompleted(_completed);
}

InformationDialog::InformationDialog(Anime::Anime& anime, std::function<void()> accept, enum Pages page, QWidget* parent)
    : QDialog(parent) {
    /* ack. lots of brackets here, but MUCH, MUCH MUCH better than what it used to be */
	setFixedSize(842, 613);
	setWindowTitle(tr("Anime Information"));
	setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);

	{
		QPalette pal;
		pal.setColor(QPalette::Window, pal.color(QPalette::Base));
		setPalette(pal);
	}

	QVBoxLayout* full_layout = new QVBoxLayout(this);

	{
		/* this handles the actual page. */
		QWidget* widget = new QWidget(this);
		QHBoxLayout* layout = new QHBoxLayout(widget);

		{
			/* Sidebar */
			QWidget* sidebar = new QWidget(widget);
			QVBoxLayout* sidebar_layout = new QVBoxLayout(sidebar);
			{
				/* Poster */
				Poster* poster = new Poster(anime, sidebar);
				sidebar_layout->addWidget(poster);
			}
			sidebar_layout->setContentsMargins(0, 0, 0, 0);
			sidebar_layout->addStretch();
			layout->addWidget(sidebar);
		}

		{
			/* ... everything else. */
			QWidget* main_widget = new QWidget(widget);
			QVBoxLayout* main_layout = new QVBoxLayout(main_widget);

			main_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

			{
				/* Anime title */
				TextWidgets::Title* anime_title =
				    new TextWidgets::Title(Strings::ToQString(anime.GetUserPreferredTitle()), main_widget);
				main_layout->addWidget(anime_title);
			}

			{
				/* Tab widget, contains main info and settings */
				QTabWidget* tabbed_widget = new QTabWidget(main_widget);
				tabbed_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

				{
					/* Main information */
					AnimeInfoWidget* main_information_widget = new AnimeInfoWidget(anime, tabbed_widget);
					tabbed_widget->addTab(main_information_widget, tr("Main information"));
				}

				{
					/* My list and settings */
					QWidget* settings_widget = new QWidget(tabbed_widget);
					settings_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);

					QVBoxLayout* settings_layout = new QVBoxLayout(settings_widget);
					settings_layout->addWidget(new TextWidgets::Header(tr("Anime list"), settings_widget));

					{
						/* Anime List */
						QWidget* sg_anime_list_content = new QWidget(settings_widget);

						constexpr int LAYOUT_HORIZ_SPACING = 25;
						constexpr int LAYOUT_VERT_SPACING  = 5;
						constexpr int LAYOUT_ITEM_WIDTH    = 175;

						QVBoxLayout* al_layout = new QVBoxLayout(sg_anime_list_content);
						al_layout->setSpacing(LAYOUT_VERT_SPACING);
						al_layout->setContentsMargins(12, 0, 0, 0);

						/* Helper function for creating sections, reduces clutter. */
						const auto CREATE_SECTION = [](QWidget* parent, std::function<void(QWidget*, QGridLayout*)> x) {
							QWidget* section = new QWidget(parent);
							QGridLayout* layout = new QGridLayout(section);
							layout->setHorizontalSpacing(LAYOUT_HORIZ_SPACING);
							layout->setVerticalSpacing(LAYOUT_VERT_SPACING);
							layout->setContentsMargins(0, 0, 0, 0);
							x(section, layout);
							parent->layout()->addWidget(section);
						};

						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
							{
								/* Episodes watched... */
								layout->addWidget(new QLabel(tr("Episodes watched:"), section), 0, 0);

								QSpinBox* spin_box = new QSpinBox(section);
								connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int i) { _progress = i; });
								spin_box->setRange(0, anime.GetEpisodes());
								spin_box->setSingleStep(1);
								spin_box->setValue(_progress = anime.GetUserProgress());
								spin_box->setFixedWidth(LAYOUT_ITEM_WIDTH);
								layout->addWidget(spin_box, 1, 0);
							}

							{
								/* Rewatching? */
								QCheckBox* checkbox = new QCheckBox(tr("Rewatching"));
								connect(checkbox, QOverload<int>::of(&QCheckBox::stateChanged), this,
								        [this](int state) { _rewatching = (state == Qt::Checked); });
								checkbox->setCheckState((_rewatching = anime.GetUserIsRewatching()) ? Qt::Checked : Qt::Unchecked);
								checkbox->setFixedWidth(LAYOUT_ITEM_WIDTH);
								layout->addWidget(checkbox, 1, 1);
							}
							layout->setColumnStretch(layout->columnCount(), 1);
						});

						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
							{
								/* Status */
								layout->addWidget(new QLabel(tr("Status:"), section), 0, 0);

								QComboBox* combo_box = new QComboBox(section);

								for (unsigned int i = 0; i < Anime::ListStatuses.size(); i++)
									combo_box->addItem(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])), static_cast<int>(Anime::ListStatuses[i]));

								connect(combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this, combo_box](int) {
									_status = static_cast<Anime::ListStatus>(combo_box->currentData().toInt());
								});

								/* this should NEVER, EVER, be NOT_IN_LIST */
								combo_box->setCurrentIndex(static_cast<int>(_status = anime.GetUserStatus()) - 1);
								combo_box->setFixedWidth(LAYOUT_ITEM_WIDTH);
								layout->addWidget(combo_box, 1, 0);
							}

							{
								/* Score */
								layout->addWidget(new QLabel(tr("Score:"), section), 0, 1);

								QSpinBox* spin_box = new QSpinBox(section);
								connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int i) {
									_score = i;
								});
								spin_box->setRange(0, 100);
								spin_box->setSingleStep(5);
								spin_box->setValue(_score = anime.GetUserScore());
								spin_box->setFixedWidth(LAYOUT_ITEM_WIDTH);
								layout->addWidget(spin_box, 1, 1);
							}
							layout->setColumnStretch(layout->columnCount(), 1);
						});

						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
							layout->addWidget(new QLabel(tr("Notes:"), section), 0, 0);

							QLineEdit* line_edit = new QLineEdit(section);
							connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
								/* this sucks but I don't really want to implement anything smarter :) */
								_notes = Strings::ToUtf8String(text);
							});
							line_edit->setText(Strings::ToQString(_notes = anime.GetUserNotes()));
							line_edit->setPlaceholderText(tr("Enter your notes about this anime"));
							layout->addWidget(line_edit, 1, 0);
						});

						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
							/* Started */
							{
								layout->addWidget(new QLabel(tr("Date started:"), section), 0, 0);

								OptionalDate* date = new OptionalDate(true, section);
								connect(date, &OptionalDate::DataChanged, this,
								        [this](bool enabled, Date date) { _started = enabled ? date : Date(); });
								date->setFixedWidth(LAYOUT_ITEM_WIDTH);
								_started = anime.GetUserDateStarted();
								if (!_started.IsValid()) {
									date->SetEnabled(false);
									_started = anime.GetAirDate();
								}
								date->SetDate(_started);
								layout->addWidget(date, 1, 0);
							}

							/* Completed */
							{
								layout->addWidget(new QLabel(tr("Date completed:"), section), 0, 1);

								OptionalDate* date = new OptionalDate(true, section);
								connect(date, &OptionalDate::DataChanged, this,
								        [this](bool enabled, Date date) { _completed = enabled ? date : Date(); });
								date->setFixedWidth(LAYOUT_ITEM_WIDTH);
								_completed = anime.GetUserDateCompleted();
								if (!_completed.IsValid()) {
									date->SetEnabled(false);
									_completed = anime.GetAirDate();
								}
								date->SetDate(_completed);
								layout->addWidget(date, 1, 1);
							}
							layout->setColumnStretch(layout->columnCount(), 1);
						});

						settings_layout->addWidget(sg_anime_list_content);
					}

					/*
					{
						// commenting this out until it actually gets implemented :)

						settings_layout->addWidget(new TextWidgets::Header(tr("Local settings"), settings_widget));

						QWidget* sg_local_content = new QWidget(settings_widget);
						QVBoxLayout* sg_local_layout = new QVBoxLayout(sg_local_content);
						sg_local_layout->setSpacing(5);
						sg_local_layout->setContentsMargins(12, 0, 0, 0);

						CREATE_SECTION(sg_local_content, [this, &anime](QWidget* section, QGridLayout* layout){
							layout->addWidget(new QLabel(tr("Alternative titles:"), section), 0, 0);

							QLineEdit* line_edit = new QLineEdit("", section);
							line_edit->setPlaceholderText(
							    tr("Enter alternative titles here, separated by a semicolon (i.e. Title 1; Title 2)"));
							layout->addWidget(line_edit, 1, 0);

							QCheckBox* checkbox = new QCheckBox(tr("Use the first alternative title to search for torrents"));
							layout->addWidget(checkbox, 2, 0);
						});

						settings_layout->addWidget(sg_local_content);
					}
					*/

					settings_layout->addStretch();

					tabbed_widget->addTab(settings_widget, tr("My list and settings"));
				}
				tabbed_widget->setCurrentIndex(static_cast<int>(page));
				main_layout->addWidget(tabbed_widget);
				main_layout->setContentsMargins(0, 0, 0, 0);
				main_layout->setSpacing(12);
			}

			layout->addWidget(main_widget);
		}
		layout->setSpacing(12);
		full_layout->addWidget(widget);
	}

	{
		/* Dialog box buttons */
		QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
		connect(button_box, &QDialogButtonBox::accepted, this, [this, accept, &anime] {
			SaveData(anime);
			accept();
			QDialog::accept();
		});
		connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
		full_layout->addWidget(button_box, 0, Qt::AlignBottom);
	}
}

void InformationDialog::showEvent(QShowEvent* event) {
	QDialog::showEvent(event);
#ifdef WIN32
	win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme());
#endif
}

#include "gui/dialog/moc_information.cpp"