changeset 187:9613d72b097e

*: multiple performance improvements like marking `static const` when it makes sense... date: change old stupid heap-based method to a structure which should make copying the thing actually make a copy. also many performance-based changes, like removing the std::tie dependency and forward-declaring nlohmann json *: replace every instance of QString::fromUtf8 to Strings::ToQString. the main difference is that our function will always convert exactly what is in the string, while some other times it would only convert up to the nearest NUL byte
author Paper <mrpapersonic@gmail.com>
date Wed, 06 Dec 2023 13:43:54 -0500
parents 6ef31dbb90ca
children 168382a89b21
files include/core/date.h src/core/anime_db.cc src/core/date.cc src/core/strings.cc src/gui/dialog/settings/application.cc src/gui/locale.cc src/gui/pages/anime_list.cc src/gui/pages/statistics.cc src/gui/translate/anilist.cc src/gui/translate/anime.cc src/gui/widgets/anime_info.cc src/gui/widgets/poster.cc src/services/anilist.cc
diffstat 13 files changed, 126 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/include/core/date.h	Wed Dec 06 11:47:59 2023 -0500
+++ b/include/core/date.h	Wed Dec 06 13:43:54 2023 -0500
@@ -1,9 +1,9 @@
 #ifndef __core__date_h
 #define __core__date_h
 
-#include "core/json.h"
-#include <QDate>
-#include <cstdint>
+#include "json/json_fwd.hpp"
+
+class QDate;
 
 class Date {
 	public:
@@ -31,9 +31,22 @@
 
 	private:
 		/* this implementation sucks and we should really use a struct instead */
-		std::shared_ptr<unsigned int> year;
-		std::shared_ptr<unsigned int> month;
-		std::shared_ptr<unsigned int> day;
+		template<typename T>
+		struct OptionalNumber {
+			public:
+				T Get() const { return enabled ? num : 0; }
+				bool Enabled() const { return enabled; }
+				void Set(T n) { num = n; enabled = true; }
+				void Void() { num = 0; enabled = false; }
+
+			protected:
+				T num = 0;
+				bool enabled = false;
+		};
+
+		OptionalNumber<unsigned int> year;
+		OptionalNumber<unsigned int> month;
+		OptionalNumber<unsigned int> day;
 };
 
 #endif // __core__date_h
--- a/src/core/anime_db.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/core/anime_db.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -7,6 +7,8 @@
 #include "gui/translate/anime.h"
 #include "gui/translate/anilist.h"
 
+#include <QDate>
+
 #include <fstream>
 
 #include <iostream>
--- a/src/core/date.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/core/date.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -1,14 +1,17 @@
 #include "core/date.h"
 #include "core/json.h"
+
 #include <QDate>
 #include <QDebug>
+
 #include <algorithm>
-#include <cstdint>
-#include <tuple>
 
 /* An implementation of AniList's "fuzzy date" */
 
-#define CLAMP(x, low, high) (std::max(low, std::min(high, x)))
+template<typename T>
+bool CLAMP(T x, T low, T high) {
+	return std::max(low, std::min(high, x));
+}
 
 Date::Date() {
 }
@@ -31,7 +34,7 @@
 
 Date::Date(const nlohmann::json& json) {
 	/* NOTE: this constructor is made for use with
-	   AniList FussyDate-style JSON. In the future, some other
+	   AniList FuzzyDate-style JSON. In the future, some other
 	   methods may be parsed and whatnot if necessary. */
 	if (json.contains("year") && json.at("year").is_number())
 		SetYear(json.at("year").get<unsigned int>());
@@ -42,58 +45,50 @@
 }
 
 void Date::VoidYear() {
-	year.reset();
+	year.Void();
 }
 
 void Date::VoidMonth() {
-	month.reset();
+	month.Void();
 }
 
 void Date::VoidDay() {
-	day.reset();
+	day.Void();
 }
 
 void Date::SetYear(unsigned int y) {
-	year.reset(new unsigned int(y));
+	year.Set(y);
 }
 
 void Date::SetMonth(unsigned int m) {
-	month.reset(new unsigned int(CLAMP(m, 1U, 12U)));
+	month.Set(CLAMP(m, 1U, 12U));
 }
 
 void Date::SetDay(unsigned int d) {
-	day.reset(new unsigned int(CLAMP(d, 1U, 31U)));
+	day.Set(CLAMP(d, 1U, 31U));
 }
 
 unsigned int Date::GetYear() const {
-	unsigned int* ptr = year.get();
-	if (ptr != nullptr)
-		return *year;
-	return -1;
+	return year.Get();
 }
 
 unsigned int Date::GetMonth() const {
-	unsigned int* ptr = month.get();
-	if (ptr != nullptr)
-		return *month;
-	return -1;
+	return month.Get();
 }
 
 unsigned int Date::GetDay() const {
-	unsigned int* ptr = day.get();
-	if (ptr != nullptr)
-		return *day;
-	return -1;
+	return day.Get();
 }
 
 bool Date::IsValid() const {
-	return year.get() && month.get() && day.get();
+	return year.Enabled() && month.Enabled() && day.Enabled();
 }
 
 bool Date::operator<(const Date& other) const {
-	unsigned int y = GetYear(), m = GetMonth(), d = GetDay();
-	unsigned int o_y = other.GetYear(), o_m = other.GetMonth(), o_d = other.GetDay();
-	return std::tie(y, m, d) < std::tie(o_y, o_m, o_d);
+	const unsigned int y = GetYear(), m = GetMonth(), d = GetDay();
+	const unsigned int o_y = other.GetYear(), o_m = other.GetMonth(), o_d = other.GetDay();
+
+	return (y < o_y && m < o_m && d < o_d);
 }
 
 bool Date::operator>(const Date& other) const {
@@ -109,26 +104,30 @@
 }
 
 QDate Date::GetAsQDate() const {
-	/* QDates don't support "missing" values, for good reason. */
-	if (IsValid())
-		return QDate(*year, *month, *day);
-	else
-		return QDate();
+	/* QDate doesn't support "missing" values (for good reason),
+	 * so we do our best and return what we can.
+	*/
+
+	return QDate(year.Enabled() ? year.Get() : 2000, month.Enabled() ? month.Get() : 1, day.Enabled() ? day.Get() : 1);
 }
 
 nlohmann::json Date::GetAsAniListJson() const {
 	nlohmann::json result = {};
-	if (year.get())
-		result["year"] = *year;
+
+	if (year.Enabled())
+		result["year"] = year.Get();
 	else
 		result["year"] = nullptr;
-	if (month.get())
-		result["month"] = *month;
+
+	if (month.Enabled())
+		result["month"] = month.Get();
 	else
 		result["month"] = nullptr;
-	if (day.get())
-		result["day"] = *day;
+
+	if (day.Enabled())
+		result["day"] = day.Get();
 	else
 		result["day"] = nullptr;
+
 	return result;
 }
--- a/src/core/strings.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/core/strings.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -2,10 +2,13 @@
  * strings.cpp: Useful functions for manipulating strings
  **/
 #include "core/strings.h"
+#include "core/session.h" // locale
+
 #include <QByteArray>
 #include <QDebug>
 #include <QString>
 #include <QLocale>
+
 #include <algorithm>
 #include <cctype>
 #include <codecvt>
@@ -20,12 +23,15 @@
 std::string Implode(const std::vector<std::string>& vector, const std::string& delimiter) {
 	if (vector.size() < 1)
 		return "-";
+
 	std::string out = "";
+
 	for (unsigned long long i = 0; i < vector.size(); i++) {
 		out.append(vector.at(i));
 		if (i < vector.size() - 1)
 			out.append(delimiter);
 	}
+
 	return out;
 }
 
@@ -113,20 +119,15 @@
 /* let Qt handle the heavy lifting of locale shit
    I don't want to deal with */
 std::string ToUpper(const std::string& string) {
-	/* todo: this "locale" will have to be moved to session.h
-	   it also defaults to en-US, which sucks very much for
-	   anyone who doesn't speak american english... */
-	QLocale locale;
-	return ToUtf8String(locale.toUpper(ToQString(string)));
+	return ToUtf8String(session.config.locale.GetLocale().toUpper(ToQString(string)));
 }
 
 std::string ToLower(const std::string& string) {
-	QLocale locale;
-	return ToUtf8String(locale.toLower(ToQString(string)));
+	return ToUtf8String(session.config.locale.GetLocale().toLower(ToQString(string)));
 }
 
 std::wstring ToWstring(const std::string& string) {
-	std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
+	static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
 	return converter.from_bytes(string);
 }
 
@@ -137,12 +138,12 @@
 }
 
 std::string ToUtf8String(const std::wstring& wstring) {
-	std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
+	static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
 	return converter.to_bytes(wstring);
 }
 
 std::string ToUtf8String(const QString& string) {
-	QByteArray ba = string.toUtf8();
+	const QByteArray ba = string.toUtf8();
 	return std::string(ba.constData(), ba.size());
 }
 
@@ -173,7 +174,7 @@
 bool ToBool(const std::string& s, const bool def) {
 	if (s.length() < 4)
 		return def;
-	std::string l = Strings::ToLower(s);
+	const std::string l = Strings::ToLower(s);
 	if (Strings::BeginningMatchesSubstring(l, "true"))
 		return true;
 	else if (Strings::BeginningMatchesSubstring(l, "false"))
@@ -186,7 +187,7 @@
 }
 
 uint64_t HumanReadableSizeToBytes(const std::string& str) {
-	const std::unordered_map<std::string, uint64_t> bytes_map = {
+	static const std::unordered_map<std::string, uint64_t> bytes_map = {
 		{"KB", 1ull << 10},
 		{"MB", 1ull << 20},
 		{"GB", 1ull << 30},
@@ -222,6 +223,7 @@
 	for (unsigned long long i = 0; i < str.length() && i < sub.length(); i++)
 		if (str[i] != sub[i])
 			return false;
+
 	return true;
 }
 
--- a/src/gui/dialog/settings/application.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/dialog/settings/application.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -128,6 +128,25 @@
 		}
 
 		{
+			/* Application theme */
+			{
+				QLabel* theme_combo_box_label = new QLabel(tr("Application theme:"), appearance_group_box);
+				appearance_layout->addWidget(theme_combo_box_label);
+			}
+
+			{
+				QComboBox* theme_combo_box = new QComboBox(appearance_group_box);
+				theme_combo_box->addItem(tr("Default"));
+				theme_combo_box->addItem(tr("Light"));
+				theme_combo_box->addItem(tr("Dark"));
+				connect(theme_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+					    [this](int index) { theme = static_cast<Themes>(index); });
+				theme_combo_box->setCurrentIndex(static_cast<int>(theme));
+				appearance_layout->addWidget(theme_combo_box);
+			}
+		}
+
+		{
 			/* Hopefully I made this easy to parse... */
 			QCheckBox* hl_above_anime_box = new QCheckBox(tr("Display highlighted anime above others"), appearance_group_box);
 			hl_above_anime_box->setCheckState(highlighted_anime_above_others ? Qt::Checked : Qt::Unchecked);
--- a/src/gui/locale.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/locale.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -101,6 +101,7 @@
 
 	if (!translator.load(path))
 		return false;
+
 	qApp->installTranslator(&translator);
 	return true;
 }
--- a/src/gui/pages/anime_list.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/pages/anime_list.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -18,6 +18,7 @@
 #include "gui/translate/anime.h"
 #include "services/services.h"
 
+#include <QDate>
 #include <QDebug>
 #include <QHBoxLayout>
 #include <QHeaderView>
--- a/src/gui/pages/statistics.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/pages/statistics.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -1,5 +1,6 @@
 #include "gui/pages/statistics.h"
 #include "core/anime_db.h"
+#include "core/strings.h"
 #include "core/session.h"
 #include "gui/pages/anime_list.h"
 #include "gui/widgets/text.h"
@@ -140,7 +141,7 @@
 		_score_distribution_graph->AddItem(i, GetTotalWithScore(i));
 
 	string = "";
-	ts << QString::fromUtf8(SecondsToDateString(session.uptime() / 1000).c_str()) << '\n';
+	ts << Strings::ToQString(SecondsToDateString(session.uptime() / 1000)) << '\n';
 	ts << session.GetRequests();
 	/* Application */
 	// UiUtils::SetPlainTextEditData(application_data, QString::number(session.uptime() / 1000));
--- a/src/gui/translate/anilist.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/translate/anilist.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -1,10 +1,11 @@
 #include "gui/translate/anilist.h"
+#include <unordered_map>
 
 namespace Translate {
 namespace AniList {
 
 Anime::SeriesStatus ToSeriesStatus(std::string status) {
-	const std::unordered_map<std::string, Anime::SeriesStatus> map = {
+	static const std::unordered_map<std::string, Anime::SeriesStatus> map = {
 	    {"FINISHED",         Anime::SeriesStatus::FINISHED        },
 	    {"RELEASING",        Anime::SeriesStatus::RELEASING       },
 	    {"NOT_YET_RELEASED", Anime::SeriesStatus::NOT_YET_RELEASED},
@@ -18,7 +19,7 @@
 }
 
 Anime::SeriesSeason ToSeriesSeason(std::string season) {
-	const std::unordered_map<std::string, Anime::SeriesSeason> map = {
+	static const std::unordered_map<std::string, Anime::SeriesSeason> map = {
 	    {"WINTER", Anime::SeriesSeason::WINTER},
 	    {"SPRING", Anime::SeriesSeason::SPRING},
 	    {"SUMMER", Anime::SeriesSeason::SUMMER},
@@ -31,7 +32,7 @@
 }
 
 Anime::SeriesFormat ToSeriesFormat(std::string format) {
-	const std::unordered_map<std::string, enum Anime::SeriesFormat> map = {
+	static const std::unordered_map<std::string, enum Anime::SeriesFormat> map = {
 	    {"TV",       Anime::SeriesFormat::TV      },
         {"TV_SHORT", Anime::SeriesFormat::TV_SHORT},
 	    {"MOVIE",    Anime::SeriesFormat::MOVIE   },
--- a/src/gui/translate/anime.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/translate/anime.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -83,7 +83,7 @@
 }
 
 Anime::ListStatus ToListStatus(const std::string& str) {
-	const std::unordered_map<std::string, Anime::ListStatus> map = {
+	static const std::unordered_map<std::string, Anime::ListStatus> map = {
 	    {"Currently watching", Anime::ListStatus::CURRENT},
 	    {"Plan to watch", Anime::ListStatus::PLANNING},
 	    {"Completed", Anime::ListStatus::COMPLETED},
@@ -97,7 +97,7 @@
 }
 
 Anime::SeriesStatus ToSeriesStatus(const std::string& str) {
-	const std::unordered_map<std::string, Anime::SeriesStatus> map = {
+	static const std::unordered_map<std::string, Anime::SeriesStatus> map = {
 	    {"Currently airing", Anime::SeriesStatus::RELEASING},
 	    {"Finished airing", Anime::SeriesStatus::FINISHED},
 	    {"Not yet aired", Anime::SeriesStatus::NOT_YET_RELEASED},
@@ -111,7 +111,7 @@
 }
 
 Anime::SeriesSeason ToSeriesSeason(const std::string& str) {
-	const std::unordered_map<std::string, Anime::SeriesSeason> map = {
+	static const std::unordered_map<std::string, Anime::SeriesSeason> map = {
 	    {"Winter", Anime::SeriesSeason::WINTER},
 	    {"Summer", Anime::SeriesSeason::SUMMER},
 	    {"Fall", Anime::SeriesSeason::FALL},
@@ -124,7 +124,7 @@
 }
 
 Anime::SeriesFormat ToSeriesFormat(const std::string& str) {
-	const std::unordered_map<std::string, Anime::SeriesFormat> map = {
+	static const std::unordered_map<std::string, Anime::SeriesFormat> map = {
 	    {"TV", Anime::SeriesFormat::TV},
 	    {"TV short", Anime::SeriesFormat::TV_SHORT},
 	    {"OVA", Anime::SeriesFormat::OVA},
@@ -140,7 +140,7 @@
 }
 
 Anime::Services ToService(const std::string& str) {
-	const std::unordered_map<std::string, Anime::Services> map = {
+	static const std::unordered_map<std::string, Anime::Services> map = {
 	    {"AniList", Anime::Services::ANILIST}
 	};
 
@@ -150,7 +150,7 @@
 }
 
 Anime::TitleLanguage ToLanguage(const std::string& str) {
-	const std::unordered_map<std::string, Anime::TitleLanguage> map = {
+	static const std::unordered_map<std::string, Anime::TitleLanguage> map = {
 	    {"Romaji", Anime::TitleLanguage::ROMAJI},
 	    {"Native", Anime::TitleLanguage::NATIVE},
 	    {"English", Anime::TitleLanguage::ENGLISH}
@@ -162,7 +162,7 @@
 }
 
 Anime::ScoreFormat ToScoreFormat(const std::string& str) {
-	const std::unordered_map<std::string, Anime::ScoreFormat> map = {
+	static const std::unordered_map<std::string, Anime::ScoreFormat> map = {
 	    {"POINT_3", Anime::ScoreFormat::POINT_3},
 	    {"POINT_5", Anime::ScoreFormat::POINT_5},
 	    {"POINT_10", Anime::ScoreFormat::POINT_10},
@@ -175,7 +175,7 @@
 	return map.at(str);
 }
 
-/* Localized versions of ToString() functions */
+/* Localized versions of ToString() functions. Meant for display to the user. */
 
 std::string ToLocalString(const Anime::ListStatus status) {
 	switch (status) {
@@ -243,4 +243,15 @@
 	}
 }
 
+std::string ToLocalString(const Anime::ScoreFormat format) {
+	switch (format) {
+		case Anime::ScoreFormat::POINT_3: return Strings::ToUtf8String(QCoreApplication::tr("3-point"));
+		case Anime::ScoreFormat::POINT_5: return Strings::ToUtf8String(QCoreApplication::tr("5-point"));
+		case Anime::ScoreFormat::POINT_10: return Strings::ToUtf8String(QCoreApplication::tr("10-point"));
+		case Anime::ScoreFormat::POINT_10_DECIMAL: return Strings::ToUtf8String(QCoreApplication::tr("10-point (Decimal)"));
+		default:
+		case Anime::ScoreFormat::POINT_100: return Strings::ToUtf8String(QCoreApplication::tr("100-point"));
+	}
+}
+
 } // namespace Translate
--- a/src/gui/widgets/anime_info.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/widgets/anime_info.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -32,8 +32,9 @@
 	QString details_data;
 	QTextStream details_data_s(&details_data);
 	/* we have to convert ALL of these strings to
-	   QString because QTextStream sucks and assumes
-	   Latin1 (on Windows?) */
+	 * QString because QTextStream sucks and assumes
+	 * Latin1 (on Windows?)
+	*/
 	details_data_s << Strings::ToQString(Translate::ToString(anime.GetFormat())) << "\n"
 	               << anime.GetEpisodes() << "\n"
 	               << Strings::ToQString(Translate::ToString(anime.GetUserStatus())) << "\n"
--- a/src/gui/widgets/poster.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/gui/widgets/poster.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -51,7 +51,7 @@
 }
 
 void Poster::RenderToLabel() {
-	QPixmap pixmap = QPixmap::fromImage(img);
+	const QPixmap pixmap = QPixmap::fromImage(img);
 	if (pixmap.isNull())
 		return;
 	label->setPixmap(pixmap.scaled(label->size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
--- a/src/services/anilist.cc	Wed Dec 06 11:47:59 2023 -0500
+++ b/src/services/anilist.cc	Wed Dec 06 13:43:54 2023 -0500
@@ -7,12 +7,15 @@
 #include "core/session.h"
 #include "core/strings.h"
 #include "gui/translate/anilist.h"
+
+#include <QDate>
 #include <QByteArray>
 #include <QDesktopServices>
 #include <QInputDialog>
 #include <QLineEdit>
 #include <QMessageBox>
 #include <QUrl>
+
 #include <chrono>
 #include <exception>
 
@@ -72,7 +75,7 @@
 }
 
 void ParseListStatus(std::string status, Anime::Anime& anime) {
-	std::unordered_map<std::string, Anime::ListStatus> map = {
+	static const std::unordered_map<std::string, Anime::ListStatus> map = {
 		{"CURRENT",   Anime::ListStatus::CURRENT  },
 		{"PLANNING",  Anime::ListStatus::PLANNING },
 		{"COMPLETED", Anime::ListStatus::COMPLETED},
@@ -91,7 +94,7 @@
 		return;
 	}
 
-	anime.SetUserStatus(map[status]);
+	anime.SetUserStatus(map.at(status));
 }
 
 std::string ListStatusToString(const Anime::Anime& anime) {