view src/gui/pages/seasons.cc @ 326:10096c5489e3

anime/db: refactor anime_season_db out
author Paper <paper@paper.us.eu.org>
date Thu, 13 Jun 2024 00:42:00 -0400
parents 78929794e7d8
children b5d6c27c308f
line wrap: on
line source

#include "gui/pages/seasons.h"

#include "core/anime_db.h"
#include "core/strings.h"
#include "gui/widgets/anime_button.h"
#include "gui/translate/anime.h"
#include "services/services.h"

#include <QDate>
#include <QFrame>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMenu>
#include <QToolBar>
#include <QToolButton>
#include <QVBoxLayout>

SeasonsPageSearchThread::SeasonsPageSearchThread(QObject* parent) : QThread(parent) {
}

void SeasonsPageSearchThread::AddToQueue(Anime::SeriesSeason season, Date::Year year) {
	queue_mutex_.lock();
	queue_.push({season, year});
	queue_mutex_.unlock();
}

void SeasonsPageSearchThread::run() {
	queue_mutex_.lock();

	while (!queue_.empty() && !isInterruptionRequested()) {
		Season season = queue_.front();

		/* unlock the mutex for a long blocking operation, so items
		 * can be added without worry */
		queue_mutex_.unlock();

		if (Services::GetSeason(season.season, season.year))
			emit ReceivedSeason(season.season, season.year);

		queue_mutex_.lock();

		queue_.pop();
	}

	queue_mutex_.unlock();
}

static SeasonsPageSearchThread search_thread_;

/* ------------------------------------------------------------------------------------- */

static constexpr Date::Year GetClosestDecade(Date::Year year) {
	return year - (year % 10);
}

void SeasonsPage::Refresh() {
	setUpdatesEnabled(false);

	if (!buttons || !season_button)
		return;

	buttons->clear();

	for (const auto& id : Anime::db.GetAllAnimeForSeason(season_, year_)) {
		QListWidgetItem* item = new QListWidgetItem;
		AnimeButton* button = new AnimeButton(this);
		button->SetAnime(Anime::db.items[id]);
		item->setSizeHint(button->sizeHint());
		buttons->addItem(item);
		buttons->setItemWidget(item, button);
	}

	season_button->setText(Strings::ToQString(Translate::ToLocalString(season_)) + " " + QString::number(year_));

	setUpdatesEnabled(true);
}

void SeasonsPage::SetSeason(Anime::SeriesSeason season, Date::Year year) {
	season_ = season;
	year_ = year;

	Refresh();
}

SeasonsPage::SeasonsPage(QWidget* parent) : QFrame(parent) {
	setBackgroundRole(QPalette::Base);
	setFrameShape(QFrame::Box);
	setFrameShadow(QFrame::Sunken);

	QVBoxLayout* full_layout = new QVBoxLayout(this);

	{
		/* Toolbar */
		QToolBar* toolbar = new QToolBar(this);
		toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
		toolbar->setIconSize(QSize(16, 16));
		toolbar->setMovable(false);

		{
			static constexpr Date::Year last_year = 1960;

			auto create_year_menu = [this](QWidget* parent, QMenu* parent_menu, Date::Year year){
				const QString year_s = QString::number(year);

				QMenu* menu = new QMenu(year_s, parent);
				for (const auto& season : Anime::SeriesSeasons) {
					QAction* action = menu->addAction(Strings::ToQString(Translate::ToLocalString(season)) + " " + year_s);
					connect(action, &QAction::triggered, this, [this, season, year] {
						SetSeason(season, year);
					});
				}
				parent_menu->addMenu(menu);
			};

			auto create_decade_menu = [create_year_menu](QWidget* parent, QMenu* parent_menu, Date::Year decade) {
				QMenu* menu = new QMenu(QString::number(decade) + "s", parent);
				for (int i = 9; i >= 0; i--)
					create_year_menu(parent, menu, decade + i);
				parent_menu->addMenu(menu);
			};

			/* we'll be extinct by the time this code breaks, so I guess it's fine :) */
			const Date::Year current_year = static_cast<Date::Year>(QDate::currentDate().year());
			const Date::Year year_before_collapse = GetClosestDecade(current_year) - 10;
			season_button = new QToolButton(toolbar);
			QMenu* full_season_menu = new QMenu(season_button);

			for (Date::Year c = current_year; c >= year_before_collapse; c--)
				create_year_menu(season_button, full_season_menu, c);

			full_season_menu->addSeparator();

			for (Date::Year c = year_before_collapse - 10; c >= last_year; c -= 10)
				create_decade_menu(season_button, full_season_menu, c);

			season_button->setMenu(full_season_menu);
			season_button->setPopupMode(QToolButton::InstantPopup);

			toolbar->addWidget(season_button);
		}

		toolbar->addSeparator();

		{
			toolbar->addAction(QIcon(":/icons/16x16/arrow-circle-315.png"), tr("Refresh data"), [this]{
				search_thread_.AddToQueue(season_, year_);
				if (!search_thread_.isRunning())
					search_thread_.start();
			});
		}

		toolbar->addSeparator();

		{
			QToolButton* button = new QToolButton(toolbar);

			{
				/* links */
				QMenu* menu = new QMenu(button);
				menu->addAction(tr("Airing status"));
				menu->addAction(tr("List status"));
				menu->addAction(tr("Type"));
				button->setMenu(menu);
			}

			button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
			button->setIcon(QIcon(":/icons/16x16/category.png"));
			button->setText(tr("Group by:"));
			button->setPopupMode(QToolButton::InstantPopup);
			toolbar->addWidget(button);
		}

		{
			QToolButton* button = new QToolButton(toolbar);

			{
				/* links */
				QMenu* menu = new QMenu(button);
				menu->addAction(tr("Airing date"));
				menu->addAction(tr("Episodes"));
				menu->addAction(tr("Popularity"));
				menu->addAction(tr("Score"));
				menu->addAction(tr("Title"));
				button->setMenu(menu);
			}

			button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
			button->setIcon(QIcon(":/icons/16x16/sort-quantity-descending.png"));
			button->setText(tr("Sort by:"));
			button->setPopupMode(QToolButton::InstantPopup);
			toolbar->addWidget(button);
		}

		{
			QToolButton* button = new QToolButton(toolbar);

			{
				/* links */
				QMenu* menu = new QMenu(button);
				menu->addAction(tr("Images"));
				menu->addAction(tr("Details"));
				button->setMenu(menu);
			}

			button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
			button->setIcon(QIcon(":/icons/16x16/ui-scroll-pane-detail.png"));
			button->setText(tr("View:"));
			button->setPopupMode(QToolButton::InstantPopup);
			toolbar->addWidget(button);
		}

		full_layout->addWidget(toolbar);
	}

	{
		QFrame* line = new QFrame(this);
		line->setFrameShape(QFrame::HLine);
		line->setFrameShadow(QFrame::Sunken);
		line->setLineWidth(1);
		full_layout->addWidget(line);
	}

	{
		buttons = new QListWidget(this);
		buttons->setFlow(QListView::LeftToRight);
		buttons->setWrapping(true);
		buttons->setContentsMargins(4, 4, 4, 4);
		buttons->setSpacing(2);
		buttons->setResizeMode(QListView::Adjust);

		full_layout->addWidget(buttons);
	}

	full_layout->setContentsMargins(0, 0, 0, 0);
	full_layout->setSpacing(0);

	connect(&search_thread_, &SeasonsPageSearchThread::ReceivedSeason, this, [this](Anime::SeriesSeason season, Date::Year year) {
		if (season == season_ && year == year_)
			Refresh();
	});

	/* Do NOT move this up in this function, buttons HAS to be initialized */
	SetSeason(Anime::SeriesSeason::Summer, 2011U);
}