changeset 108:2004b41d4a59

*: huge commit 1. WORKING LOCALIZATION + translation for Spanish and British English 2. idk like 2 changes for the dark theme :)
author Paper <mrpapersonic@gmail.com>
date Sun, 05 Nov 2023 23:31:49 -0500
parents 49c8d1976869
children 79714c95a145
files CMakeLists.txt include/core/config.h include/core/session.h include/gui/dialog/about.h include/gui/dialog/information.h include/gui/dialog/settings.h include/gui/pages/anime_list.h include/gui/theme.h rc/win32/version.rc.in src/core/config.cc src/gui/dialog/about.cc src/gui/dialog/information.cc src/gui/dialog/settings.cc src/gui/dialog/settings/application.cc src/gui/dialog/settings/services.cc src/gui/pages/anime_list.cc src/gui/theme.cc src/gui/window.cc src/main.cc src/track/media.cc
diffstat 20 files changed, 674 insertions(+), 471 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Sun Nov 05 17:44:49 2023 -0500
+++ b/CMakeLists.txt	Sun Nov 05 23:31:49 2023 -0500
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.16)
+cmake_minimum_required(VERSION 3.18)
 project(minori LANGUAGES CXX VERSION 0.1.0)
 
 if(APPLE)
@@ -10,7 +10,8 @@
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
 
 option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
-option(USE_QT6 "Build with Qt 6 instead of Qt 5" OFF)
+option(USE_QT6 "Force build with Qt 6" OFF)
+option(USE_QT5 "Force build with Qt 5" OFF)
 
 add_subdirectory(dep/anitomy)
 add_subdirectory(dep/animia)
@@ -19,25 +20,25 @@
 # Fix for mingw64
 list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a")
 
-if(USE_QT6)
-	find_package(Qt6 COMPONENTS Widgets REQUIRED)
+if (USE_QT6)
+	set(QT_VERSION_MAJOR 6)
+elseif(USE_Qt5)
+	set(QT_VERSION_MAJOR 5)
 else()
-	find_package(Qt5 COMPONENTS Widgets REQUIRED)
+	find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
 endif()
+
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
+
 find_package(CURL REQUIRED)
 
 set(LIBRARIES
 	${CURL_LIBRARIES}
+	${Qt${QT_VERSION_MAJOR}Widgets_LIBRARIES}
 	anitomy
 	animia
 )
 
-if(USE_QT6)
-	list(APPEND LIBRARIES ${Qt6Widgets_LIBRARIES})
-else()
-	list(APPEND LIBRARIES ${Qt5Widgets_LIBRARIES})
-endif()
-
 # We need Cocoa for some OS X stuff
 if(APPLE)
 	find_library(COCOA_LIBRARY Cocoa)
@@ -62,6 +63,7 @@
 	# Main window
 	src/gui/window.cc
 	src/gui/theme.cc
+	src/gui/locale.cc
 
 	# Main window pages
 	src/gui/pages/anime_list.cc
@@ -105,6 +107,41 @@
 	rc/dark.qrc
 )
 
+set(INCLUDE
+	include
+	dep/pugixml/src
+	dep/animia/include
+	dep/anitomy
+	dep
+)
+
+set(TS_FILES
+	rc/locale/en_GB.ts
+	rc/locale/es.ts
+)
+
+set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/rc/locale")
+
+# dumb little hack to get this working on Qt5 and Qt6
+cmake_language(CALL qt${QT_VERSION_MAJOR}_create_translation ${SRC_FILES} ${TS_FILES} OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}/include")
+cmake_language(CALL qt${QT_VERSION_MAJOR}_add_translation QM_FILES ${TS_FILES})
+list(APPEND SRC_FILES ${QM_FILES})
+
+function(qt_create_resource_file outfile)
+	set(QRC "<!DOCTYPE rcc><RCC version=\"1.0\">\n\t<qresource>\n")
+	get_filename_component(DIR ${outfile} DIRECTORY)
+	foreach (qm ${ARGN})
+		file(RELATIVE_PATH name ${DIR} ${qm})
+		string(APPEND QRC "\t\t<file>${name}</file>\n")
+	endforeach()
+	string(APPEND QRC "\t</qresource>\n</RCC>\n")
+	file(WRITE ${outfile} ${QRC})
+endfunction()
+
+qt_create_resource_file("${CMAKE_CURRENT_BINARY_DIR}/rc/locale.qrc" ${QM_FILES})
+list(APPEND SRC_FILES "${CMAKE_CURRENT_BINARY_DIR}/rc/locale.qrc")
+
+# This is also used in the Win32 rc file
 set(RC_INFO_STRING "A lightweight anime tracker built with Qt.")
 
 if(APPLE) # Mac OS X (or OS X (or macOS))
@@ -140,12 +177,8 @@
 set_property(TARGET minori PROPERTY AUTOMOC ON)
 set_property(TARGET minori PROPERTY AUTORCC ON)
 
-target_include_directories(minori PUBLIC ${CURL_INCLUDE_DIRS} PRIVATE include dep/pugixml/src dep/animia/include dep/anitomy dep)
-if(USE_QT6)
-	target_include_directories(minori PUBLIC ${Qt6Widgets_INCLUDE_DIRS})
-else()
-	target_include_directories(minori PUBLIC ${Qt5Widgets_INCLUDE_DIRS})
-endif()
+target_include_directories(minori PUBLIC ${CURL_INCLUDE_DIRS} PRIVATE ${INCLUDE})
+target_include_directories(minori PUBLIC ${Qt${QT_VERSION_MAJOR}Widgets_INCLUDE_DIRS})
 target_compile_options(minori PRIVATE -Wall -Wpedantic -Wextra -Wsuggest-override -Wold-style-cast)
 if(APPLE)
 	target_compile_definitions(minori PUBLIC MACOSX)
--- a/include/core/config.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/core/config.h	Sun Nov 05 23:31:49 2023 -0500
@@ -3,6 +3,7 @@
 
 #include "core/anime.h"
 #include "gui/theme.h"
+#include "gui/locale.h"
 
 class Config {
 	public:
@@ -11,6 +12,7 @@
 
 		Anime::Services service;
 		Theme::Theme theme;
+		Locale::Locale locale;
 
 		struct {
 			public:
--- a/include/core/session.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/core/session.h	Sun Nov 05 23:31:49 2023 -0500
@@ -2,6 +2,7 @@
 #define __core__session_h
 
 #include "core/config.h"
+#include "gui/locale.h"
 #include <QElapsedTimer>
 
 struct Session {
--- a/include/gui/dialog/about.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/gui/dialog/about.h	Sun Nov 05 23:31:49 2023 -0500
@@ -4,8 +4,13 @@
 #include <QDialog>
 
 class AboutWindow final : public QDialog {
+	Q_OBJECT
+
 	public:
 		AboutWindow(QWidget* parent = nullptr);
+
+	protected:
+		void showEvent(QShowEvent* event) override;
 };
 
 #endif // __gui__dialog__about_h
--- a/include/gui/dialog/information.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/gui/dialog/information.h	Sun Nov 05 23:31:49 2023 -0500
@@ -12,6 +12,9 @@
 	public:
 		InformationDialog(Anime::Anime& anime, std::function<void()> accept, QWidget* parent = nullptr);
 
+	protected:
+		void showEvent(QShowEvent* event) override;
+
 	private:
 		void SaveData(Anime::Anime& anime);
 		unsigned int _progress;
--- a/include/gui/dialog/settings.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/gui/dialog/settings.h	Sun Nov 05 23:31:49 2023 -0500
@@ -5,6 +5,7 @@
 #include "core/config.h"
 #include <QDialog>
 #include <QWidget>
+#include <QLocale>
 
 class QLabel;
 class QTabWidget;
@@ -26,6 +27,8 @@
 };
 
 class SettingsPageServices final : public SettingsPage {
+		Q_OBJECT
+
 	public:
 		SettingsPageServices(QWidget* parent = nullptr);
 		void SaveInfo() override;
@@ -38,6 +41,8 @@
 };
 
 class SettingsPageApplication final : public SettingsPage {
+		Q_OBJECT
+
 	public:
 		SettingsPageApplication(QWidget* parent = nullptr);
 		void SaveInfo() override;
@@ -45,6 +50,7 @@
 	private:
 		QWidget* CreateAnimeListWidget();
 		Themes theme;
+		QLocale locale;
 		Anime::TitleLanguage language;
 		bool display_aired_episodes;
 		bool display_available_episodes;
@@ -60,6 +66,9 @@
 		QWidget* CreateServicesMainPage(QWidget* parent);
 		void OnOK();
 
+	protected:
+		void showEvent(QShowEvent* event) override;
+
 	private:
 		SideBar* sidebar;
 		QStackedWidget* stacked;
--- a/include/gui/pages/anime_list.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/gui/pages/anime_list.h	Sun Nov 05 23:31:49 2023 -0500
@@ -11,16 +11,6 @@
 class QTreeView;
 class QTabBar;
 
-class AnimeListPageDelegate final : public QStyledItemDelegate {
-		Q_OBJECT
-
-	public:
-		explicit AnimeListPageDelegate(QObject* parent);
-
-		QWidget* createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const override;
-		void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
-};
-
 class AnimeListPageSortFilter final : public QSortFilterProxyModel {
 		Q_OBJECT
 
--- a/include/gui/theme.h	Sun Nov 05 17:44:49 2023 -0500
+++ b/include/gui/theme.h	Sun Nov 05 23:31:49 2023 -0500
@@ -16,7 +16,7 @@
 		Theme(Themes theme = Themes::OS);
 		void SetTheme(Themes theme);
 		Themes GetTheme();
-		bool IsInDarkMode();
+		bool IsInDarkTheme();
 		void RepaintCurrentTheme();
 
 	private:
--- a/rc/win32/version.rc.in	Sun Nov 05 17:44:49 2023 -0500
+++ b/rc/win32/version.rc.in	Sun Nov 05 23:31:49 2023 -0500
@@ -6,7 +6,7 @@
 #define VER_PRODUCTVERSION     @minori_VERSION_MAJOR@,@minori_VERSION_MINOR@,@minori_VERSION_PATCH@,0
 #define VER_PRODUCTVERSION_STR "@minori_VERSION_MAJOR@.@minori_VERSION_MINOR@.@minori_VERSION_PATCH@\0"
 
-#define RC_INFO_STRING "@RC_INFO_STRING@"
+#define RC_INFO_STRING "@RC_INFO_STRING@\0"
 
 VS_VERSION_INFO VERSIONINFO
 	FILEVERSION VER_FILEVERSION
--- a/src/core/config.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/core/config.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -41,6 +41,7 @@
 	file.read(ini);
 
 	service = Translate::ToService(ini.get("General").get("Service"));
+	locale.SetActiveLocale(QLocale(Strings::ToQString(ini.get("General").get("Locale"))));
 	anime_list.language = Translate::ToLanguage(ini.get("Anime List").get("Title language"));
 	anime_list.display_aired_episodes = string_to_bool(ini.get("Anime List").get("Display only aired episodes"), true);
 	anime_list.display_available_episodes = string_to_bool(ini.get("Anime List").get("Display only available episodes in library"), true);
@@ -62,6 +63,7 @@
 	mINI::INIStructure ini;
 
 	ini["General"]["Service"] = Translate::ToString(service);
+	ini["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name());
 	ini["Anime List"]["Title language"] = Translate::ToString(anime_list.language);
 	ini["Anime List"]["Display only aired episodes"] = bool_to_string(anime_list.display_aired_episodes);
 	ini["Anime List"]["Display only available episodes in library"] = bool_to_string(anime_list.display_available_episodes);
--- a/src/gui/dialog/about.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/dialog/about.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -1,6 +1,7 @@
 #include "gui/dialog/about.h"
 #include "core/json.h"
 #include "core/version.h"
+#include "core/session.h"
 #include "core/strings.h"
 #include "gui/widgets/text.h"
 #include "pugixml.hpp"
@@ -9,13 +10,18 @@
 #include <QTextBrowser>
 #include <QTextCharFormat>
 #include <QTextCursor>
+#include <QCoreApplication>
 #include <curl/curl.h>
+#ifdef WIN32
+#include "sys/win32/dark_theme.h"
+#endif
 
 template <typename T, size_t N>
 constexpr size_t array_size(T (&)[N]) {
 	return N;
 }
 
+/* used for JSON for Modern C++ */
 #define CONCAT_VERSION_NX(major, minor, patch) "v" #major "." #minor "." #patch
 #define CONCAT_VERSION(major, minor, patch) CONCAT_VERSION_NX(major, minor, patch)
 
@@ -35,59 +41,76 @@
 	return data->version;
 }
 
-/* I hate HTML so much... */
-static const QString html = QString(
-	"<body>"
-	"  <h2 style=\"font-weight: normal;\"><strong>Minori</strong> " MINORI_VERSION "</h2>"
-	"  <p>"
-	"    <strong>Author:</strong><br>"
-	"    Paper (@mrpapersonic)"
-	"  </p>"
-	"  <p>"
-	"    <strong>Third party components:</strong><br>"
-	    "<a href=\"https://curl.se/\">libcurl v") + get_curl_version() + "</a>"
-	    ", "
-	    "<a href=\"https://p.yusukekamiyamane.com/\">Fugue Icons v3.5.6</a>"
-	    ", "
-	    "<a href=\"https://github.com/erengy/anitomy\">Anitomy</a>"
-	    ", "
-	    "<a href=\"https://github.com/nlohmann/json\">JSON for Modern C++ " CONCAT_VERSION(NLOHMANN_JSON_VERSION_MAJOR,
-		                                                                                   NLOHMANN_JSON_VERSION_MINOR,
-		                                                                                   NLOHMANN_JSON_VERSION_PATCH) "</a>"
-	    ", "
-	    "<a href=\"https://pugixml.org/\">pugixml v" + pugixml_version + "</a>"
-	    ", "
-	    "<a href=\"https://github.com/pulzed/mINI\">mINI v0.9.14</a>"
-	"  </p>"
-	"<span>"
-	"<strong>Special thanks:</strong>"
-	"</span>"
-	"  <ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 15px; margin-right: 0px; -qt-list-indent:0;\">"
-	"    <li><strong>Eren Okka</strong> for creating Taiga</li>"
-	"    <li><strong>Alex Huszagh</strong> and <strong>Colin Duquesnoy</strong> for "
-	        "creating BreezeStyleSheets, on which the dark theme in this program is "
-	        "based off of</li>"
-	"    <li><strong>Andy Brice</strong> for making "
-	"  </ul>"
-	"</body>";
-
 AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) {
 	setMinimumSize(641, 325);
 	setWindowTitle(tr("About Minori"));
 	setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
-	QHBoxLayout* layout = new QHBoxLayout(this);
-
-	QPalette pal = QPalette();
-	pal.setColor(QPalette::Window, pal.color(QPalette::Base));
-	setPalette(pal);
 	setAutoFillBackground(true);
 
-	QTextBrowser* paragraph = new QTextBrowser(this);
-	paragraph->setOpenExternalLinks(true);
-	paragraph->setFrameShape(QFrame::NoFrame);
-	paragraph->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-	paragraph->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-	paragraph->setHtml(html);
+	QHBoxLayout* layout = new QHBoxLayout(this);
 
-	layout->addWidget(paragraph);
+	/* we have to generate this on-the-fly for localization purposes */
+	static const QString html = QString(
+		"<body>"
+		"  <h2 style=\"font-weight: normal;\"><strong>Minori</strong> " MINORI_VERSION "</h2>"
+		"  <p>"
+		"    <strong>" + QCoreApplication::tr("Author:") + "</strong><br>"
+		"    Paper (@mrpapersonic)"
+		"  </p>"
+		"  <p>"
+		"    <strong>" + QCoreApplication::tr("Third party components:") + "</strong><br>"
+		    "<a href=\"https://curl.se/\">libcurl v") + get_curl_version() + "</a>"
+		    ", "
+		    "<a href=\"https://p.yusukekamiyamane.com/\">Fugue Icons v3.5.6</a>"
+		    ", "
+		    "<a href=\"https://github.com/erengy/anitomy\">Anitomy</a>"
+		    ", "
+		    "<a href=\"https://github.com/nlohmann/json\">JSON for Modern C++ " CONCAT_VERSION(NLOHMANN_JSON_VERSION_MAJOR,
+			                                                                                   NLOHMANN_JSON_VERSION_MINOR,
+			                                                                                   NLOHMANN_JSON_VERSION_PATCH) "</a>"
+		    ", "
+		    "<a href=\"https://pugixml.org/\">pugixml v" + pugixml_version + "</a>"
+		    ", "
+		    "<a href=\"https://github.com/pulzed/mINI\">mINI v0.9.14</a>"
+		"  </p>"
+		"<span>"
+		"<strong>" + QCoreApplication::tr("Special thanks:") + "</strong>"
+		"</span>"
+		"  <ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 15px; margin-right: 0px; -qt-list-indent:0;\">"
+		"    <li><strong>Eren Okka</strong> " + QCoreApplication::tr("for creating Taiga") + "</li>"
+		"    <li><strong>Alex Huszagh</strong> " + QCoreApplication::tr("and") + " <strong>Colin Duquesnoy</strong> " +
+		        QCoreApplication::tr("for creating BreezeStyleSheets, on which the dark theme in this program is "
+		        "based off of") + "</li>"
+		"    <li><strong>Andy Brice</strong> " + QCoreApplication::tr("for providing some sample code for "
+		        "detecting dark mode on Windows and macOS") + "</li>"
+		"    <li><strong>Manuel Wudka-Robles</strong> " + QCoreApplication::tr("for providing information on "
+		        "getting open file descriptors on macOS") + "</li>"
+		"  </ul>"
+		"</body>";
+
+	{
+		QPalette pal = QPalette();
+		pal.setColor(QPalette::Window, pal.color(QPalette::Base));
+		setPalette(pal);
+	}
+
+	{
+		QTextBrowser* paragraph = new QTextBrowser(this);
+		paragraph->setOpenExternalLinks(true);
+		paragraph->setFrameShape(QFrame::NoFrame);
+		paragraph->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+		paragraph->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+		paragraph->setHtml(html);
+
+		layout->addWidget(paragraph);
+	}
 }
+
+void AboutWindow::showEvent(QShowEvent* event) {
+	QDialog::showEvent(event);
+#ifdef WIN32
+	win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme());
+#endif
+}
+
+#include "gui/dialog/moc_about.cpp"
--- a/src/gui/dialog/information.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/dialog/information.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -2,6 +2,7 @@
 #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"
@@ -22,6 +23,9 @@
 #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. */
@@ -37,6 +41,7 @@
 
 InformationDialog::InformationDialog(Anime::Anime& anime, std::function<void()> accept, 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);
@@ -47,214 +52,259 @@
 		setPalette(pal);
 	}
 
-	QWidget* widget = new QWidget(this);
+	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);
 
-	/* "sidebar", includes... just the anime image :) */
-	QWidget* sidebar = new QWidget(widget);
-	QVBoxLayout* sidebar_layout = new QVBoxLayout(sidebar);
-	Poster* poster = new Poster(anime, sidebar);
-	sidebar_layout->addWidget(poster);
-	sidebar_layout->setContentsMargins(0, 0, 0, 0);
-	sidebar_layout->addStretch();
+			{
+				/* 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 widget */
-	QWidget* main_widget = new QWidget(widget);
+				{
+					/* Main information */
+					AnimeInfoWidget* main_information_widget = new AnimeInfoWidget(anime, tabbed_widget);
+					tabbed_widget->addTab(main_information_widget, tr("Main information"));
+				}
 
-	main_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+				{
+					/* My list and settings */
+					QWidget* settings_widget = new QWidget(tabbed_widget);
+					settings_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
 
-	/* anime title header text */
-	TextWidgets::Title* anime_title =
-	    new TextWidgets::Title(Strings::ToQString(anime.GetUserPreferredTitle()), main_widget);
+					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);
 
-	/* tabbed widget */
-	QTabWidget* tabbed_widget = new QTabWidget(main_widget);
-	tabbed_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+						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);
 
-	/* main info tab */
-	AnimeInfoWidget* main_information_widget = new AnimeInfoWidget(anime, tabbed_widget);
+						/* 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);
+						};
 
-	QWidget* settings_widget = new QWidget(tabbed_widget);
-	settings_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+						CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
+							{
+								/* Episodes watched... */
+								layout->addWidget(new QLabel(tr("Episodes watched:"), section), 0, 0);
 
-	QVBoxLayout* settings_layout = new QVBoxLayout(settings_widget);
-	settings_layout->addWidget(new TextWidgets::Header(tr("Anime list"), settings_widget));
-
-	QWidget* sg_anime_list_content = new QWidget(settings_widget);
+								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);
+							}
 
-	constexpr int LAYOUT_HORIZ_SPACING = 25;
-	constexpr int LAYOUT_VERT_SPACING  = 5;
-	constexpr int LAYOUT_ITEM_WIDTH    = 175;
+							{
+								/* 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);
+						});
 
-	QVBoxLayout* al_layout = new QVBoxLayout(sg_anime_list_content);
-	al_layout->setSpacing(LAYOUT_VERT_SPACING);
-	al_layout->setContentsMargins(12, 0, 0, 0);
+						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());
+								});
 
-	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);
-	};
+								/* 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);
 
-	CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
-		/* Episodes watched... */
-		layout->addWidget(new QLabel(tr("Episodes watched:"), 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);
 
-		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);
+								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);
 
-		/* 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);
-	});
+								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);
+						});
 
-	CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){
-		/* Status */
-		layout->addWidget(new QLabel(tr("Status:"), section), 0, 0);
+						settings_layout->addWidget(sg_anime_list_content);
+					}
+
+					/*
+					{
+						// commenting this out until it actually gets implemented :)
 
-		QComboBox* combo_box = new QComboBox(section);
+						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);
 
-		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]));
+							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();
 
-		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);
+					tabbed_widget->addTab(settings_widget, tr("My list and settings"));
+				}
+				main_layout->addWidget(tabbed_widget);
+				main_layout->setContentsMargins(0, 0, 0, 0);
+				main_layout->setSpacing(12);
+			}
 
-		/* Score */
-		layout->addWidget(new QLabel(tr("Score:"), section), 0, 1);
+			layout->addWidget(main_widget);
+		}
+		layout->setSpacing(12);
+		full_layout->addWidget(widget);
+	}
 
-		QSpinBox* spin_box = new QSpinBox(section);
-		connect(spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int i) {
-			_score = i;
+	{
+		/* 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();
 		});
-		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);
-
-		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);
-	});
+		connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+		full_layout->addWidget(button_box, 0, Qt::AlignBottom);
+	}
+}
 
-	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);
-*/
-#undef CREATE_SECTION
-#undef CREATE_FULL_WIDTH_SECTION
-
-	settings_layout->addStretch();
-
-	tabbed_widget->addTab(main_information_widget, tr("Main information"));
-	tabbed_widget->addTab(settings_widget, tr("My list and settings"));
-
-	QVBoxLayout* main_layout = new QVBoxLayout(main_widget);
-	main_layout->addWidget(anime_title);
-	main_layout->addWidget(tabbed_widget);
-	main_layout->setContentsMargins(0, 0, 0, 0);
-	main_layout->setSpacing(12);
-
-	QHBoxLayout* layout = new QHBoxLayout(widget);
-	layout->addWidget(sidebar);
-	layout->addWidget(main_widget);
-	layout->setSpacing(12);
-
-	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);
-
-	QVBoxLayout* buttons_layout = new QVBoxLayout(this);
-	buttons_layout->addWidget(widget);
-	buttons_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"
--- a/src/gui/dialog/settings.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/dialog/settings.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -1,4 +1,5 @@
 #include "gui/dialog/settings.h"
+#include "core/session.h"
 #include "gui/widgets/sidebar.h"
 #include "gui/widgets/text.h"
 #include <QDialogButtonBox>
@@ -7,6 +8,9 @@
 #include <QStackedWidget>
 #include <QVBoxLayout>
 #include <QWidget>
+#ifdef WIN32
+#include "sys/win32/dark_theme.h"
+#endif
 
 SettingsPage::SettingsPage(QWidget* parent, QString title) : QWidget(parent) {
 	setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
@@ -60,48 +64,68 @@
 	QDialog::accept();
 }
 
+void SettingsDialog::showEvent(QShowEvent* event) {
+	QDialog::showEvent(event);
+#ifdef WIN32
+	win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme());
+#endif
+}
+
 SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) {
 	setFixedSize(755, 566);
 	setWindowTitle(tr("Settings"));
 	setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
-	QWidget* widget = new QWidget(this);
-	widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
-	sidebar = new SideBar(widget);
-	sidebar->setCurrentItem(sidebar->AddItem(tr("Services"), SideBar::CreateIcon(":/icons/24x24/globe.png")));
-	// sidebar->AddItem(tr("Library"), SideBar::CreateIcon(":/icons/24x24/inbox-film.png"));
-	sidebar->AddItem(tr("Application"), SideBar::CreateIcon(":/icons/24x24/application-sidebar-list.png"));
-	// sidebar->AddItem(tr("Recognition"), SideBar::CreateIcon(":/icons/24x24/question.png"));
-	// sidebar->AddItem(tr("Sharing"), SideBar::CreateIcon(":/icons/24x24/megaphone.png"));
-	// sidebar->AddItem(tr("Torrents"), SideBar::CreateIcon(":/icons/24x24/feed.png"));
-	// sidebar->AddItem(tr("Advanced"), SideBar::CreateIcon(":/icons/24x24/gear.png"));
-	sidebar->setIconSize(QSize(24, 24));
-	sidebar->setFrameShape(QFrame::Box);
+
+	QVBoxLayout* full_layout = new QVBoxLayout(this);
+
+	{
+		QWidget* widget = new QWidget(this);
+		widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+		QHBoxLayout* layout = new QHBoxLayout(widget);
+
+		{
+			sidebar = new SideBar(widget);
 
-	QPalette pal(sidebar->palette());
-	sidebar->SetBackgroundColor(pal.color(QPalette::Base));
+			sidebar->setCurrentItem(sidebar->AddItem(tr("Services"), SideBar::CreateIcon(":/icons/24x24/globe.png")));
+			// sidebar->AddItem(tr("Library"), SideBar::CreateIcon(":/icons/24x24/inbox-film.png"));
+			sidebar->AddItem(tr("Application"), SideBar::CreateIcon(":/icons/24x24/application-sidebar-list.png"));
+			// sidebar->AddItem(tr("Recognition"), SideBar::CreateIcon(":/icons/24x24/question.png"));
+			// sidebar->AddItem(tr("Sharing"), SideBar::CreateIcon(":/icons/24x24/megaphone.png"));
+			// sidebar->AddItem(tr("Torrents"), SideBar::CreateIcon(":/icons/24x24/feed.png"));
+			// sidebar->AddItem(tr("Advanced"), SideBar::CreateIcon(":/icons/24x24/gear.png"));
 
-	sidebar->setFixedWidth(158);
-	sidebar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+			sidebar->setIconSize(QSize(24, 24));
+			sidebar->setFrameShape(QFrame::Box);
+
+			QPalette pal(sidebar->palette());
+			sidebar->SetBackgroundColor(pal.color(QPalette::Base));
 
-	stacked = new QStackedWidget(this);
-	stacked->addWidget(new SettingsPageServices(stacked));
-	stacked->addWidget(new SettingsPageApplication(stacked));
-	stacked->setCurrentIndex(0);
+			sidebar->setFixedWidth(158);
+			sidebar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+			layout->addWidget(sidebar);
+		}
 
-	connect(sidebar, &QListWidget::currentRowChanged, stacked, &QStackedWidget::setCurrentIndex);
+		{
+			stacked = new QStackedWidget(widget);
+			stacked->addWidget(new SettingsPageServices(stacked));
+			stacked->addWidget(new SettingsPageApplication(stacked));
+			stacked->setCurrentIndex(0);
+
+			connect(sidebar, &QListWidget::currentRowChanged, stacked, &QStackedWidget::setCurrentIndex);
 
-	QHBoxLayout* layout = new QHBoxLayout(widget);
-	layout->addWidget(sidebar);
-	layout->addWidget(stacked);
-	layout->setContentsMargins(0, 0, 0, 0);
+			layout->addWidget(stacked);
+		}
+
+		layout->setContentsMargins(0, 0, 0, 0);
+		full_layout->addWidget(widget);
+	}
 
-	QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
-	connect(button_box, &QDialogButtonBox::accepted, this, &SettingsDialog::OnOK);
-	connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
-
-	QVBoxLayout* buttons_layout = new QVBoxLayout(this);
-	buttons_layout->addWidget(widget);
-	buttons_layout->addWidget(button_box);
+	{
+		QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
+		connect(button_box, &QDialogButtonBox::accepted, this, &SettingsDialog::OnOK);
+		connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+		full_layout->addWidget(button_box);
+	}
 }
 
 #include "gui/dialog/moc_settings.cpp"
--- a/src/gui/dialog/settings/application.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/dialog/settings/application.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -1,6 +1,8 @@
 #include "core/session.h"
+#include "core/strings.h"
 #include "gui/dialog/settings.h"
 #include "gui/theme.h"
+#include "gui/locale.h"
 #include <QCheckBox>
 #include <QComboBox>
 #include <QGroupBox>
@@ -9,109 +11,178 @@
 #include <QPushButton>
 #include <QSizePolicy>
 #include <QVBoxLayout>
+#include <algorithm>
 
 QWidget* SettingsPageApplication::CreateAnimeListWidget() {
 	QWidget* result = new QWidget(this);
 	result->setAutoFillBackground(true);
 	result->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
 
-	QGroupBox* actions_group_box = new QGroupBox(tr("Actions"), result);
-	actions_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+	QVBoxLayout* full_layout = new QVBoxLayout(result);
 
-	/* Actions/Double click */
-	QWidget* double_click_widget = new QWidget(actions_group_box);
-	QLabel* dc_combo_box_label = new QLabel(tr("Double click:"), double_click_widget);
-	QComboBox* dc_combo_box = new QComboBox(double_click_widget);
-	dc_combo_box->addItem(tr("View anime info"));
+	{
+		/* Actions */
+		QGroupBox* actions_group_box = new QGroupBox(tr("Actions"), result);
+		actions_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+		QHBoxLayout* actions_layout = new QHBoxLayout(actions_group_box);
 
-	QVBoxLayout* double_click_layout = new QVBoxLayout(double_click_widget);
-	double_click_layout->addWidget(dc_combo_box_label);
-	double_click_layout->addWidget(dc_combo_box);
-	double_click_layout->setContentsMargins(0, 0, 0, 0);
+		{
+			/* Actions/Double click */
+			QWidget* double_click_widget = new QWidget(actions_group_box);
+			QLabel* dc_combo_box_label = new QLabel(tr("Double click:"), double_click_widget);
+			QComboBox* dc_combo_box = new QComboBox(double_click_widget);
+			dc_combo_box->addItem(tr("View anime info"));
 
-	/* Actions/Middle click */
-	QWidget* middle_click_widget = new QWidget(actions_group_box);
-	QLabel* mc_combo_box_label = new QLabel(tr("Middle click:"), middle_click_widget);
-	QComboBox* mc_combo_box = new QComboBox(middle_click_widget);
-	mc_combo_box->addItem(tr("Play next episode"));
+			QVBoxLayout* double_click_layout = new QVBoxLayout(double_click_widget);
+			double_click_layout->addWidget(dc_combo_box_label);
+			double_click_layout->addWidget(dc_combo_box);
+			double_click_layout->setContentsMargins(0, 0, 0, 0);
+
+			actions_layout->addWidget(double_click_widget);
+		}
+
+		{
+			/* Actions/Middle click */
+			QWidget* middle_click_widget = new QWidget(actions_group_box);
+			QLabel* mc_combo_box_label = new QLabel(tr("Middle click:"), middle_click_widget);
+			QComboBox* mc_combo_box = new QComboBox(middle_click_widget);
+			mc_combo_box->addItem(tr("Play next episode"));
 
-	QVBoxLayout* middle_click_layout = new QVBoxLayout(middle_click_widget);
-	middle_click_layout->addWidget(mc_combo_box_label);
-	middle_click_layout->addWidget(mc_combo_box);
-	middle_click_layout->setContentsMargins(0, 0, 0, 0);
+			QVBoxLayout* middle_click_layout = new QVBoxLayout(middle_click_widget);
+			middle_click_layout->addWidget(mc_combo_box_label);
+			middle_click_layout->addWidget(mc_combo_box);
+			middle_click_layout->setContentsMargins(0, 0, 0, 0);
+			
+			actions_layout->addWidget(middle_click_widget);
+		}
 
-	/* Actions */
-	QHBoxLayout* actions_layout = new QHBoxLayout(actions_group_box);
-	actions_layout->addWidget(double_click_widget);
-	actions_layout->addWidget(middle_click_widget);
+		full_layout->addWidget(actions_group_box);
+	}
+
+	{
+		/* Appearance */
+		QGroupBox* appearance_group_box = new QGroupBox(tr("Appearance"), result);
+		appearance_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+		QVBoxLayout* appearance_layout = new QVBoxLayout(appearance_group_box);
 
-	QGroupBox* appearance_group_box = new QGroupBox(tr("Appearance"), result);
-	appearance_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+		{
+			/* Title language */
+			{
+				QLabel* lang_combo_box_label = new QLabel(tr("Title language preference:"), appearance_group_box);
+				appearance_layout->addWidget(lang_combo_box_label);
+			}
+			{
+				QComboBox* lang_combo_box = new QComboBox(appearance_group_box);
+				lang_combo_box->addItem(tr("Romaji"));
+				lang_combo_box->addItem(tr("Native"));
+				lang_combo_box->addItem(tr("English"));
+				connect(lang_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+				        [this](int index) { language = static_cast<Anime::TitleLanguage>(index); });
+				lang_combo_box->setCurrentIndex(static_cast<int>(language));
+				appearance_layout->addWidget(lang_combo_box);
+			}
+		}
 
-	QLabel* lang_combo_box_label = new QLabel(tr("Title language preference:"), appearance_group_box);
-	QComboBox* lang_combo_box = new QComboBox(appearance_group_box);
-	lang_combo_box->addItem(tr("Romaji"));
-	lang_combo_box->addItem(tr("Native"));
-	lang_combo_box->addItem(tr("English"));
-	connect(lang_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
-	        [this](int index) { language = static_cast<Anime::TitleLanguage>(index); });
-	lang_combo_box->setCurrentIndex(static_cast<int>(language));
+		{
+			/* Application theme */
+			{
+				QLabel* theme_combo_box_label = new QLabel(tr("Application theme:"), appearance_group_box);
+				appearance_layout->addWidget(theme_combo_box_label);
+			}
 
-	QLabel* theme_combo_box_label = new QLabel(tr("Application theme:"), appearance_group_box);
-	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));
+			{
+				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);
+			}
+		}
+
+		{
+			/* Application locale */
+			{
+				QLabel* locale_combo_box_label = new QLabel(tr("Set application locale (requires restart):"), appearance_group_box);
+				appearance_layout->addWidget(locale_combo_box_label);
+			}
 
-	QCheckBox* hl_anime_box =
-	    new QCheckBox(tr("Highlight anime if next episode is available in library folders"), appearance_group_box);
-	QCheckBox* hl_above_anime_box = new QCheckBox(tr("Display highlighted anime above others"), appearance_group_box);
-	connect(hl_anime_box, &QCheckBox::stateChanged, this, [this, hl_above_anime_box](int state) {
-		highlight_anime_if_available = !(state == Qt::Unchecked);
-		hl_above_anime_box->setEnabled(state);
-	});
-	connect(hl_above_anime_box, &QCheckBox::stateChanged, this,
-	        [this](int state) { highlight_anime_if_available = !(state == Qt::Unchecked); });
-	hl_anime_box->setCheckState(highlight_anime_if_available ? Qt::Checked : Qt::Unchecked);
-	hl_above_anime_box->setCheckState(highlighted_anime_above_others ? Qt::Checked : Qt::Unchecked);
-	hl_above_anime_box->setEnabled(hl_anime_box->checkState() != Qt::Unchecked);
-	hl_above_anime_box->setContentsMargins(10, 0, 0, 0);
+			{
+				QComboBox* locale_combo_box = new QComboBox(appearance_group_box);
+				const auto& available_locales = session.config.locale.GetAvailableLocales();
+				for (const auto& l : available_locales)
+					locale_combo_box->addItem(Strings::ToQString(Locale::GetLocaleFullName(l)), l);
+
+				connect(locale_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+					    [this, locale_combo_box](int) { locale = locale_combo_box->currentData().toLocale(); });
+
+				for (size_t i = 0; i < available_locales.size(); i++)
+					if (available_locales[i] == locale)
+						locale_combo_box->setCurrentIndex(i);
+				appearance_layout->addWidget(locale_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);
+			hl_above_anime_box->setEnabled(highlight_anime_if_available);
+			hl_above_anime_box->setContentsMargins(10, 0, 0, 0);
+
+			connect(hl_above_anime_box, &QCheckBox::stateChanged, this,
+			        [this](int state) { highlight_anime_if_available = !(state == Qt::Unchecked); });
 
-	/* Appearance */
-	QVBoxLayout* appearance_layout = new QVBoxLayout(appearance_group_box);
-	appearance_layout->addWidget(lang_combo_box_label);
-	appearance_layout->addWidget(lang_combo_box);
-	appearance_layout->addWidget(theme_combo_box_label);
-	appearance_layout->addWidget(theme_combo_box);
-	appearance_layout->addWidget(hl_anime_box);
-	appearance_layout->addWidget(hl_above_anime_box);
+			{
+				/* This is here because the above checkbox actually depends on it to be checked. */
+				QCheckBox* hl_anime_box = new QCheckBox(tr("Highlight anime if next episode is available in library folders"), appearance_group_box);
+				hl_anime_box->setCheckState(highlight_anime_if_available ? Qt::Checked : Qt::Unchecked);
 
-	QGroupBox* progress_group_box = new QGroupBox(tr("Progress"), result);
-	progress_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+				connect(hl_anime_box, &QCheckBox::stateChanged, this, [this, hl_above_anime_box](int state) {
+					highlight_anime_if_available = !(state == Qt::Unchecked);
+					hl_above_anime_box->setEnabled(state);
+				});
+
+				appearance_layout->addWidget(hl_anime_box);
+			}
+
+			appearance_layout->addWidget(hl_above_anime_box);
+		}
+
+		full_layout->addWidget(appearance_group_box);
+	}
 
-	QCheckBox* progress_display_aired_episodes =
-	    new QCheckBox(tr("Display aired episodes (estimated)"), progress_group_box);
-	connect(progress_display_aired_episodes, &QCheckBox::stateChanged, this,
-	        [this](int state) { display_aired_episodes = !(state == Qt::Unchecked); });
-	progress_display_aired_episodes->setCheckState(display_aired_episodes ? Qt::Checked : Qt::Unchecked);
+	{
+		/* Progress */
+		QGroupBox* progress_group_box = new QGroupBox(tr("Progress"), result);
+		progress_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+		QVBoxLayout* progress_layout = new QVBoxLayout(progress_group_box);
 
-	QCheckBox* progress_display_available_episodes =
-	    new QCheckBox(tr("Display available episodes in library folders"), progress_group_box);
-	connect(progress_display_available_episodes, &QCheckBox::stateChanged, this,
-	        [this](int state) { display_available_episodes = !(state == Qt::Unchecked); });
-	progress_display_available_episodes->setCheckState(display_available_episodes ? Qt::Checked : Qt::Unchecked);
+		{
+			QCheckBox* progress_display_aired_episodes =
+			    new QCheckBox(tr("Display aired episodes (estimated)"), progress_group_box);
+			connect(progress_display_aired_episodes, &QCheckBox::stateChanged, this,
+			        [this](int state) { display_aired_episodes = !(state == Qt::Unchecked); });
+			progress_display_aired_episodes->setCheckState(display_aired_episodes ? Qt::Checked : Qt::Unchecked);
+			progress_layout->addWidget(progress_display_aired_episodes);
+		}
+		{
+			QCheckBox* progress_display_available_episodes =
+			    new QCheckBox(tr("Display available episodes in library folders"), progress_group_box);
+			connect(progress_display_available_episodes, &QCheckBox::stateChanged, this,
+			        [this](int state) { display_available_episodes = !(state == Qt::Unchecked); });
+			progress_display_available_episodes->setCheckState(display_available_episodes ? Qt::Checked : Qt::Unchecked);
+			progress_layout->addWidget(progress_display_available_episodes);
+		}
 
-	QVBoxLayout* progress_layout = new QVBoxLayout(progress_group_box);
-	progress_layout->addWidget(progress_display_aired_episodes);
-	progress_layout->addWidget(progress_display_available_episodes);
+		full_layout->addWidget(progress_group_box);
+	}
 
-	QVBoxLayout* full_layout = new QVBoxLayout(result);
-	full_layout->addWidget(actions_group_box);
-	full_layout->addWidget(appearance_group_box);
-	full_layout->addWidget(progress_group_box);
 	full_layout->setSpacing(10);
 	full_layout->addStretch();
 
@@ -125,11 +196,13 @@
 	session.config.anime_list.display_aired_episodes = display_aired_episodes;
 	session.config.anime_list.display_available_episodes = display_available_episodes;
 	session.config.theme.SetTheme(theme);
+	session.config.locale.SetActiveLocale(locale);
 }
 
 SettingsPageApplication::SettingsPageApplication(QWidget* parent) : SettingsPage(parent, tr("Application")) {
 	language = session.config.anime_list.language;
 	theme = session.config.theme.GetTheme();
+	locale = session.config.locale.GetLocale();
 	highlighted_anime_above_others = session.config.anime_list.highlighted_anime_above_others;
 	highlight_anime_if_available = session.config.anime_list.highlight_anime_if_available;
 	display_aired_episodes = session.config.anime_list.display_aired_episodes;
--- a/src/gui/dialog/settings/services.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/dialog/settings/services.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -15,27 +15,37 @@
 	QWidget* result = new QWidget(this);
 	result->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
 
-	QGroupBox* sync_group_box = new QGroupBox(tr("Synchronization"), result);
-	sync_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+	QVBoxLayout* full_layout = new QVBoxLayout(result);
 
-	QLabel* sync_combo_box_label = new QLabel(tr("Active service and metadata provider:"), sync_group_box);
+	{
+		QGroupBox* sync_group_box = new QGroupBox(tr("Synchronization"), result);
+		sync_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
 
-	QComboBox* sync_combo_box = new QComboBox(sync_group_box);
-	sync_combo_box->addItem(tr("AniList"));
-	connect(sync_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
-	        [this](int index) { service = static_cast<Anime::Services>(index + 1); });
-	sync_combo_box->setCurrentIndex(static_cast<int>(service) - 1);
+		QVBoxLayout* sync_layout = new QVBoxLayout(sync_group_box);
+
+		{
+			QLabel* sync_combo_box_label = new QLabel(tr("Active service and metadata provider:"), sync_group_box);
+			sync_layout->addWidget(sync_combo_box_label);
+		}
 
-	QLabel* sync_note_label =
-	    new QLabel(tr("Note: Minori is unable to synchronize multiple services at the same time."), sync_group_box);
+		{
+			QComboBox* sync_combo_box = new QComboBox(sync_group_box);
+			sync_combo_box->addItem(tr("AniList"));
+			connect(sync_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+			        [this](int index) { service = static_cast<Anime::Services>(index + 1); });
+			sync_combo_box->setCurrentIndex(static_cast<int>(service) - 1);
+			sync_layout->addWidget(sync_combo_box);
+		}
 
-	QVBoxLayout* sync_layout = new QVBoxLayout(sync_group_box);
-	sync_layout->addWidget(sync_combo_box_label);
-	sync_layout->addWidget(sync_combo_box);
-	sync_layout->addWidget(sync_note_label);
+		{
+			QLabel* sync_note_label =
+			    new QLabel(tr("Note: Minori is unable to synchronize multiple services at the same time."), sync_group_box);
+			sync_layout->addWidget(sync_note_label);
+		}
 
-	QVBoxLayout* full_layout = new QVBoxLayout(result);
-	full_layout->addWidget(sync_group_box);
+		full_layout->addWidget(sync_group_box);
+	}
+	
 	full_layout->setSpacing(10);
 	full_layout->addStretch();
 
@@ -46,51 +56,69 @@
 	QWidget* result = new QWidget(this);
 	result->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
 
-	QGroupBox* group_box = new QGroupBox(tr("Account"), result);
-	group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+	QVBoxLayout* full_layout = new QVBoxLayout(result);
 
-	/* this is outdated! usernames are retrieved through a request to AniList now.
-	       although that's a bit... erm... cancerous, maybe this method IS useful. IDK */
-	QLabel* username_entry_label = new QLabel(tr("Username: (not your email address)"), group_box);
+	{
+		/* Account */
+		QGroupBox* group_box = new QGroupBox(tr("Account"), result);
+		group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+		QVBoxLayout* layout = new QVBoxLayout(group_box);
 
-	QWidget* auth_widget = new QWidget(group_box);
-	QLineEdit* username_entry = new QLineEdit(username, auth_widget);
-	connect(username_entry, &QLineEdit::editingFinished, this,
-	        [this, username_entry] { username = username_entry->text(); });
+		{
+			QLabel* username_entry_label = new QLabel(tr("Username: (not your email address)"), group_box);
+			layout->addWidget(username_entry_label);
+		}
 
-	QPushButton* auth_button = new QPushButton(auth_widget);
-	connect(auth_button, &QPushButton::clicked, this, [] { Services::AniList::AuthorizeUser(); });
-	auth_button->setText(session.config.anilist.auth_token.empty() ? tr("Authorize...") : tr("Re-authorize..."));
+		{
+			/* Authorization */
+			QWidget* auth_widget = new QWidget(group_box);
+			QHBoxLayout* auth_layout = new QHBoxLayout(auth_widget);
 
-	QHBoxLayout* auth_layout = new QHBoxLayout(auth_widget);
-	auth_layout->addWidget(username_entry);
-	auth_layout->addWidget(auth_button);
+			{
+				/* Username: this literally never gets used btw */
+				QLineEdit* username_entry = new QLineEdit(username, auth_widget);
+				connect(username_entry, &QLineEdit::editingFinished, this,
+				        [this, username_entry] { username = username_entry->text(); });
+				auth_layout->addWidget(username_entry);
+			}
+
+			{
+				/* The actual auth button */
+				QPushButton* auth_button = new QPushButton(auth_widget);
+				connect(auth_button, &QPushButton::clicked, this, [] { Services::AniList::AuthorizeUser(); });
+				auth_button->setText(session.config.anilist.auth_token.empty() ? tr("Authorize...") : tr("Re-authorize..."));
+				auth_layout->addWidget(auth_button);
+			}
 
-	QLabel* note_label = new QLabel(tr("<a href=\"http://anilist.co/\">Create a new AniList account</a>"), group_box);
-	note_label->setTextFormat(Qt::RichText);
-	note_label->setTextInteractionFlags(Qt::TextBrowserInteraction);
-	note_label->setOpenExternalLinks(true);
+			layout->addWidget(auth_widget);
+		}
 
-	QVBoxLayout* layout = new QVBoxLayout(group_box);
-	layout->addWidget(username_entry_label);
-	layout->addWidget(auth_widget);
-	layout->addWidget(note_label);
+		{
+			/* Note on creating new accounts... */
+			QLabel* note_label = new QLabel(tr("<a href=\"http://anilist.co/\">Create a new AniList account</a>"), group_box);
+			note_label->setTextFormat(Qt::RichText);
+			note_label->setTextInteractionFlags(Qt::TextBrowserInteraction);
+			note_label->setOpenExternalLinks(true);
+			layout->addWidget(note_label);
+		}
+		
+		full_layout->addWidget(group_box);
+	}
 
-	QVBoxLayout* full_layout = new QVBoxLayout(result);
-	full_layout->addWidget(group_box);
 	full_layout->setSpacing(10);
 	full_layout->addStretch();
 	return result;
 }
 
 void SettingsPageServices::SaveInfo() {
-	// session.config.anilist.username =
-	Strings::ToUtf8String(username);
+	// see services/anilist.cc for why this is commented out
+	// session.config.anilist.username = Strings::ToUtf8String(username);
 	session.config.service = service;
 }
 
 SettingsPageServices::SettingsPageServices(QWidget* parent) : SettingsPage(parent, tr("Services")) {
-	username = QString::fromUtf8(session.config.anilist.username.c_str());
+	// username = QString::fromUtf8(session.config.anilist.username.c_str());
 	service = session.config.service;
 	AddTab(CreateMainPage(), tr("Main"));
 	AddTab(CreateAniListPage(), tr("AniList"));
--- a/src/gui/pages/anime_list.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/pages/anime_list.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -29,42 +29,6 @@
 #include <QThreadPool>
 #include <set>
 
-AnimeListPageDelegate::AnimeListPageDelegate(QObject* parent) : QStyledItemDelegate(parent) {
-}
-
-QWidget* AnimeListPageDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const {
-	// no edit 4 u
-	return nullptr;
-}
-
-void AnimeListPageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
-                                  const QModelIndex& index) const {
-	switch (index.column()) {
-#if 0 /* I'm just commenting this out for now. Seems like a huge headache to do with Qt. */
-		case AnimeListPageModel::AL_PROGRESS: {
-			const int progress = static_cast<int>(index.data(Qt::UserRole).toReal());
-			const int episodes =
-			    static_cast<int>(index.siblingAtColumn(AnimeListPageModel::AL_EPISODES).data(Qt::UserRole).toReal());
-
-			int text_width = 59;
-			QRectF text_rect(option.rect.x() + text_width, option.rect.y(), text_width, option.decorationSize.height());
-			painter->save();
-			painter->drawText(text_rect, tr("/"), QTextOption(Qt::AlignCenter | Qt::AlignVCenter));
-			drawText(const QRectF &rectangle, const QString &text, const QTextOption &option =
-			   QTextOption()) painter->drawText(QRectF(text_rect.x(), text_rect.y(), text_width / 2 - 2,
-			   text_rect.height()), QString::number(progress), QTextOption(Qt::AlignRight | Qt::AlignVCenter));
-			   painter->drawText(
-			       QRectF(text_rect.x() + text_width / 2 + 2, text_rect.y(), text_width / 2 - 2, text_rect.height()),
-			       QString::number(episodes), QTextOption(Qt::AlignLeft | Qt::AlignVCenter));
-			   painter->restore();
-			   QStyledItemDelegate::paint(painter, option, index);
-			   break;
-		}
-#endif
-		default: QStyledItemDelegate::paint(painter, option, index); break;
-	}
-}
-
 AnimeListPageSortFilter::AnimeListPageSortFilter(QObject* parent) : QSortFilterProxyModel(parent) {
 }
 
@@ -423,7 +387,6 @@
 	/* Tree view... */
 	QWidget* tree_widget = new QWidget(this);
 	tree_view = new QTreeView(tree_widget);
-	tree_view->setItemDelegate(new AnimeListPageDelegate(tree_view));
 	tree_view->setUniformRowHeights(true);
 	tree_view->setAllColumnsShowFocus(false);
 	tree_view->setAlternatingRowColors(true);
--- a/src/gui/theme.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/theme.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -31,7 +31,7 @@
 	return theme;
 }
 
-bool Theme::IsInDarkMode() {
+bool Theme::IsInDarkTheme() {
 	if (theme != Themes::OS)
 		return (theme == Themes::DARK);
 #ifdef MACOSX
--- a/src/gui/window.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/gui/window.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -275,7 +275,7 @@
 void MainWindow::showEvent(QShowEvent* event) {
 	QMainWindow::showEvent(event);
 #ifdef WIN32
-	win32::SetTitleBarToBlack(this, session.config.theme.IsInDarkMode());
+	win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme());
 #endif
 }
 
--- a/src/main.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/main.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -2,14 +2,13 @@
 #include "gui/window.h"
 #include <QApplication>
 #include <QStyleFactory>
+#include <QTranslator>
 #include <QLocale>
 
 Session session;
 
 int main(int argc, char** argv) {
 	QApplication app(argc, argv);
-	/* this is a reasonable default, I presume */
-	QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
 
 	session.config.Load();
 
--- a/src/track/media.cc	Sun Nov 05 17:44:49 2023 -0500
+++ b/src/track/media.cc	Sun Nov 05 23:31:49 2023 -0500
@@ -4,7 +4,6 @@
 #include "anitomy/anitomy.h"
 #include "core/filesystem.h"
 #include "core/strings.h"
-#include <QDebug>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -17,14 +16,13 @@
 	std::vector<int> pids = Animia::get_all_pids();
 	for (int i : pids) {
 		for (const std::string& player : media_players) {
-			if (Animia::get_process_name(i) == player) {
-				std::vector<std::string> files = Animia::filter_system_files(Animia::get_open_files(i));
-				for (const std::string& f : files) {
-					Filesystem::Path p(f);
-					for (const std::string& ext : media_extensions) {
-						if (p.Extension() == ext)
-							return p;
-					}
+			if (Animia::get_process_name(i) != player)
+				continue;
+			for (const std::string& f : Animia::filter_system_files(Animia::get_open_files(i))) {
+				Filesystem::Path p(f);
+				for (const std::string& ext : media_extensions) {
+					if (p.Extension() == ext)
+						return p;
 				}
 			}
 		}