changeset 102:b315f3759c56

*: big patch 1. use a wrapper for mINI that enables case sensitivity (personal preference) 2. rename dark_theme.cc to theme.cc and change it to be a class 3. include the "dep" folder so we don't have stupidity in json.h or ini.h 4. I think the graph was also tweaked a lot in this, nothing is constexpr and size is found at runtime...
author Paper <mrpapersonic@gmail.com>
date Fri, 03 Nov 2023 21:32:52 -0400
parents c537996cf67b
children 621084cc542c
files CMakeLists.txt include/core/config.h include/core/ini.h include/core/json.h include/core/strings.h include/gui/dark_theme.h include/gui/theme.h include/gui/widgets/graph.h src/core/config.cc src/core/strings.cc src/gui/dark_theme.cc src/gui/dialog/settings.cc src/gui/dialog/settings/application.cc src/gui/pages/anime_list.cc src/gui/pages/now_playing.cc src/gui/pages/statistics.cc src/gui/theme.cc src/gui/widgets/text.cc src/gui/window.cc
diffstat 19 files changed, 265 insertions(+), 179 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Nov 03 14:06:02 2023 -0400
+++ b/CMakeLists.txt	Fri Nov 03 21:32:52 2023 -0400
@@ -63,7 +63,7 @@
 
 	# Main window
 	src/gui/window.cc
-	src/gui/dark_theme.cc
+	src/gui/theme.cc
 
 	# Main window pages
 	src/gui/pages/anime_list.cc
@@ -122,7 +122,7 @@
 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/mini)
+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()
--- a/include/core/config.h	Fri Nov 03 14:06:02 2023 -0400
+++ b/include/core/config.h	Fri Nov 03 21:32:52 2023 -0400
@@ -2,12 +2,7 @@
 #define __core__config_h
 
 #include "core/anime.h"
-
-enum class Themes {
-	LIGHT,
-	DARK,
-	OS // AKA "Default"
-};
+#include "gui/theme.h"
 
 class Config {
 	public:
@@ -15,7 +10,7 @@
 		int Save();
 
 		Anime::Services service;
-		Themes theme;
+		Theme::Theme theme;
 
 		struct {
 			public:
@@ -34,8 +29,12 @@
 		} anilist;
 };
 
+#define WIDEIFY_EX(x) L##x
+#define WIDEIFY(x)    WIDEIFY_EX(x)
 #define CONFIG_DIR    "minori"
+#define CONFIG_WDIR   WIDEIFY(CONFIG_DIR)
 #define CONFIG_NAME   "config.ini"
+#define CONFIG_WNAME  WIDEIFY(CONFIG_NAME)
 
 #define MAX_LINE_LENGTH 256
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/core/ini.h	Fri Nov 03 21:32:52 2023 -0400
@@ -0,0 +1,7 @@
+#ifndef __core__ini_h
+#define __core__ini_h
+
+#define MINI_CASE_SENSITIVE
+#include "mini/ini.h"
+
+#endif
\ No newline at end of file
--- a/include/core/json.h	Fri Nov 03 14:06:02 2023 -0400
+++ b/include/core/json.h	Fri Nov 03 21:32:52 2023 -0400
@@ -1,7 +1,7 @@
 #ifndef __core__json_h
 #define __core__json_h
 
-#include "../../dep/json/json.h"
+#include "json/json.h"
 
 namespace JSON {
 
--- a/include/core/strings.h	Fri Nov 03 14:06:02 2023 -0400
+++ b/include/core/strings.h	Fri Nov 03 21:32:52 2023 -0400
@@ -36,6 +36,8 @@
 /* arithmetic :) */
 int ToInt(const std::string& str, int def = 0);
 
+bool BeginningMatchesSubstring(const std::string& str, const std::string& sub);
+
 }; // namespace Strings
 
 #endif // __core__strings_h
\ No newline at end of file
--- a/include/gui/dark_theme.h	Fri Nov 03 14:06:02 2023 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#ifndef __gui__dark_theme_h
-#define __gui__dark_theme_h
-
-namespace DarkTheme {
-
-bool IsInDarkMode();
-/* this function is private, and should stay that way */
-// void SetStyleSheet(enum Themes theme);
-void SetToDarkTheme();
-void SetToLightTheme();
-enum Themes GetCurrentOSTheme();
-void SetTheme(enum Themes theme);
-
-} // namespace DarkTheme
-
-#endif // __gui__dark_theme_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/gui/theme.h	Fri Nov 03 21:32:52 2023 -0400
@@ -0,0 +1,31 @@
+#ifndef __gui__theme_h
+#define __gui__theme_h
+
+#include <QStyle>
+
+enum class Themes {
+	OS,
+	LIGHT,
+	DARK
+};
+
+namespace Theme {
+
+class Theme final {
+	public:
+		Theme(Themes theme = Themes::OS);
+		void SetTheme(Themes theme);
+		Themes GetTheme();
+
+	private:
+		bool IsInDarkMode();
+		void SetToDarkTheme();
+		void SetToLightTheme();
+		void SetStyleSheet(Themes theme);
+		Themes GetCurrentOSTheme();
+		Themes theme;
+};
+
+} // namespace Theme
+
+#endif // __gui__theme_h
--- a/include/gui/widgets/graph.h	Fri Nov 03 14:06:02 2023 -0400
+++ b/include/gui/widgets/graph.h	Fri Nov 03 21:32:52 2023 -0400
@@ -4,6 +4,7 @@
 /* This class is defined as a template, so that means everything gets defined here as well :) */
 
 #include <QWidget>
+#include <QDebug>
 #include <QSize>
 #include <QPaintEvent>
 #include <QSize>
@@ -11,36 +12,50 @@
 #include <QPainter>
 #include <QPainterPath>
 #include <QPen>
+#include <algorithm>
 #include <unordered_map>
 
 template <typename T>
 class Graph final : public QWidget {
 	public:
 		Graph(QWidget* parent = nullptr) : QWidget(parent) {};
-		void AddItem(T key, int val) { map[key] = val; update(); updateGeometry(); };
+		void AddItem(T key, unsigned long val) { map[key] = val; update(); updateGeometry(); };
 		void Clear() { map.clear(); update(); updateGeometry(); };
 
 	protected:
-		static constexpr int ITEM_HEIGHT = 20;
-		static constexpr int TEXT_WIDTH = 30;
-		inline int GetTotal() {
-			int count = 0;
+		static constexpr int SPACING = 5;
+		inline unsigned long GetTotal() {
+			unsigned long count = 0;
 			for (const auto& item : map)
 				count += item.second;
 			return count;
 		}
-		QSize minimumSizeHint() const override { return QSize(100, ITEM_HEIGHT * map.size()); };
+		QSize minimumSizeHint() const override {
+			QFontMetrics metric(font());
+			return QSize(100, metric.height() * map.size() + 2 * (map.size() - 2));
+		};
+		inline unsigned long GetTextWidth() {
+			unsigned long ret = 0;
+			QFontMetrics metric(font());
+			for (const auto& item : map) {
+				unsigned long width = metric.boundingRect(QString::number(item.first)).width();
+				if (width > ret)
+					ret = width;
+			}
+			return ret;
+		}
 		void paintEvent(QPaintEvent* event) override {
 			const QRect rect = event->rect();
-			const int height_of_each = rect.height() / map.size();
+			const int height_of_each = QFontMetrics(font()).height();
 			const int size = GetTotal();
+			const int text_width = GetTextWidth();
+			int current_y = rect.y();
 
 			/* now we do the actual painting */
 			QPainter painter(this);
 
-			int i = 0;
 			for (const auto& item : map) {
-				painter.drawText(QRect(rect.x(), rect.y() + i * ITEM_HEIGHT, TEXT_WIDTH, ITEM_HEIGHT), Qt::AlignRight | Qt::AlignVCenter, QString::number(item.first));
+				painter.drawText(QRect(rect.x(), current_y, text_width, height_of_each), Qt::AlignRight | Qt::AlignVCenter, QString::number(item.first));
 
 				if (size) {
 					painter.save();
@@ -49,16 +64,16 @@
 					painter.setPen(pen);
 
 					QPainterPath path;
-					path.addRect(rect.x()+35, rect.y() + i * ITEM_HEIGHT, (static_cast<double>(item.second)/size)*(rect.width()-35), ITEM_HEIGHT);
-					painter.fillPath(path, Qt::blue);
+					path.addRect(rect.x() + text_width + SPACING, current_y, (static_cast<double>(item.second)/size)*(rect.width() - text_width - SPACING), height_of_each);
+					painter.fillPath(path, Qt::darkBlue);
 					painter.drawPath(path);
 
 					painter.restore();
 				}
-				i++;
+				current_y += height_of_each + 2;
 			}
 		};
-		std::unordered_map<T, int> map = {};
+		std::unordered_map<T, unsigned long> map = {};
 };
 
 #endif // __gui__widgets__graph_h
\ No newline at end of file
--- a/src/core/config.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/core/config.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -5,11 +5,11 @@
 #include "core/config.h"
 #include "core/strings.h"
 #include "core/anime.h"
+#include "core/ini.h"
 #include "core/filesystem.h"
 #include "core/json.h"
 #include "gui/translate/anime.h"
 #include "gui/translate/config.h"
-#include "ini.h" // mINI
 #include <algorithm>
 #include <cstdlib>
 #include <cstring>
@@ -17,27 +17,25 @@
 #include <fstream>
 #include <limits.h>
 
-/* I'm not exactly fond of using JSON for a config file, but it's better than
-   no config I guess. I'd like to have something more readable, e.g. YAML or
-   even INI. */
-
+/* Move these to strings.cc or the translation stuff, please. */
 static bool string_to_bool(const std::string& s, bool def = false) {
-	bool b;
-    std::istringstream is(Strings::ToLower(s));
-    is >> std::boolalpha >> b;
-    return b;
+	if (s.length() < 4)
+		return def;
+	std::string l = Strings::ToLower(s);
+	if (Strings::BeginningMatchesSubstring(l, "true"))
+		return true;
+	else if (Strings::BeginningMatchesSubstring(l, "false"))
+		return false;
+	return def;
 }
 
 static std::string bool_to_string(bool b) {
-	std::ostringstream stream;
-	stream << std::boolalpha << b;
-	return stream.str();
+	return b ? "true" : "false";
 }
 
 int Config::Load() {
 	Filesystem::Path cfg_path = Filesystem::GetConfigPath();
-	if (!cfg_path.Exists())
-		return 0;
+
 	mINI::INIFile file(cfg_path.GetPath());
 	mINI::INIStructure ini;
 	file.read(ini);
@@ -50,7 +48,7 @@
 	anime_list.highlighted_anime_above_others = string_to_bool(ini.get("Anime List").get("Display highlighted anime above others"));
 	anilist.auth_token = ini.get("AniList").get("Auth Token");
 	anilist.user_id = Strings::ToInt(ini.get("AniList").get("User ID"));
-	theme = Translate::ToTheme(ini.get("Appearance").get("Theme"));
+	theme.SetTheme(Translate::ToTheme(ini.get("Appearance").get("Theme")));
 
 	return 0;
 }
@@ -71,9 +69,9 @@
 	ini["Anime List"]["Display highlighted anime above others"] = bool_to_string(anime_list.highlighted_anime_above_others);
 	ini["AniList"]["Auth Token"] = anilist.auth_token;
 	ini["AniList"]["User ID"] = std::to_string(anilist.user_id);
-	ini["Appearance"]["Theme"] = Translate::ToString(theme);
+	ini["Appearance"]["Theme"] = Translate::ToString(theme.GetTheme());
 
-	file.generate(ini);
+	file.write(ini);
 
 	return 0;
 }
--- a/src/core/strings.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/core/strings.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -12,6 +12,7 @@
 #include <locale>
 #include <string>
 #include <vector>
+#include <unordered_map>
 
 namespace Strings {
 
@@ -145,4 +146,11 @@
 	return tmp;
 }
 
+bool BeginningMatchesSubstring(const std::string& str, const std::string& sub) {
+	for (unsigned long long i = 0; i < str.length() && i < sub.length(); i++)
+		if (str[i] != sub[i])
+			return false;
+	return true;
+}
+
 } // namespace Strings
--- a/src/gui/dark_theme.cc	Fri Nov 03 14:06:02 2023 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-#include "core/config.h"
-#include "core/session.h"
-#include <QApplication>
-#include <QFile>
-#include <QTextStream>
-#ifdef MACOSX
-#	include "sys/osx/dark_theme.h"
-#else
-#	include "sys/win32/dark_theme.h"
-#endif
-
-namespace DarkTheme {
-
-bool IsInDarkMode() {
-	if (session.config.theme != Themes::OS)
-		return (session.config.theme == Themes::DARK);
-#ifdef MACOSX
-	if (osx::DarkThemeAvailable()) {
-		if (osx::IsInDarkTheme()) {
-			return true;
-		} else {
-			return false;
-		}
-	}
-#elif defined(WIN32)
-	if (win32::DarkThemeAvailable()) {
-		if (win32::IsInDarkTheme()) {
-			return true;
-		} else {
-			return false;
-		}
-	}
-#endif
-	return (session.config.theme == Themes::DARK);
-}
-
-/* this function is private, and should stay that way */
-void SetStyleSheet(enum Themes theme) {
-	switch (theme) {
-		case Themes::DARK: {
-			QFile f(":qdarkstyle/dark/darkstyle.qss");
-			if (!f.exists())
-				return; // fail
-			f.open(QFile::ReadOnly | QFile::Text);
-			QTextStream ts(&f);
-			qApp->setStyleSheet(ts.readAll());
-			break;
-		}
-		default: qApp->setStyleSheet(""); break;
-	}
-}
-
-void SetToDarkTheme() {
-	/* macOS >= 10.14 has its own global dark theme,
-	   use it :) */
-#if MACOSX
-	if (osx::DarkThemeAvailable())
-		osx::SetToDarkTheme();
-	else
-#endif
-		SetStyleSheet(Themes::DARK);
-}
-
-void SetToLightTheme() {
-#if MACOSX
-	if (osx::DarkThemeAvailable())
-		osx::SetToLightTheme();
-	else
-#endif
-		SetStyleSheet(Themes::LIGHT);
-}
-
-enum Themes GetCurrentOSTheme() {
-#if MACOSX
-	if (osx::DarkThemeAvailable())
-		return osx::IsInDarkTheme() ? Themes::DARK : Themes::LIGHT;
-#elif defined(WIN32)
-	if (win32::DarkThemeAvailable())
-		return win32::IsInDarkTheme() ? Themes::DARK : Themes::LIGHT;
-#endif
-	/* Currently OS detection only supports Windows and macOS.
-	   Please don't be shy if you're willing to port it to other OSes
-	   (or desktop environments, or window managers) */
-	return Themes::LIGHT;
-}
-
-void SetTheme(enum Themes theme) {
-	switch (theme) {
-		case Themes::LIGHT: SetToLightTheme(); break;
-		case Themes::DARK: SetToDarkTheme(); break;
-		case Themes::OS:
-			if (GetCurrentOSTheme() == Themes::LIGHT)
-				SetToLightTheme();
-			else
-				SetToDarkTheme();
-			break;
-	}
-}
-
-} // namespace DarkTheme
--- a/src/gui/dialog/settings.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/dialog/settings.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -20,10 +20,12 @@
 	font.setWeight(QFont::Bold);
 	page_title->setFont(font);
 
-	QPalette pal = page_title->palette();
-	pal.setColor(QPalette::Window, QColor(0xAB, 0xAB, 0xAB));
-	pal.setColor(QPalette::WindowText, Qt::white);
-	page_title->setPalette(pal);
+	{
+		QPalette pal = page_title->palette();
+		pal.setColor(QPalette::Window, QColor(0xAB, 0xAB, 0xAB));
+		pal.setColor(QPalette::WindowText, Qt::white);
+		page_title->setPalette(pal);
+	}
 	page_title->setAutoFillBackground(true);
 
 	page_title->setFixedHeight(23);
--- a/src/gui/dialog/settings/application.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/dialog/settings/application.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -1,6 +1,6 @@
 #include "core/session.h"
 #include "gui/dialog/settings.h"
-#include "gui/dark_theme.h"
+#include "gui/theme.h"
 #include <QCheckBox>
 #include <QComboBox>
 #include <QGroupBox>
@@ -123,13 +123,12 @@
 	session.config.anime_list.highlight_anime_if_available = highlight_anime_if_available;
 	session.config.anime_list.display_aired_episodes = display_aired_episodes;
 	session.config.anime_list.display_available_episodes = display_available_episodes;
-	session.config.theme = theme;
-	DarkTheme::SetTheme(session.config.theme);
+	session.config.theme.SetTheme(theme);
 }
 
 SettingsPageApplication::SettingsPageApplication(QWidget* parent) : SettingsPage(parent, tr("Application")) {
 	language = session.config.anime_list.language;
-	theme = session.config.theme;
+	theme = session.config.theme.GetTheme();
 	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/pages/anime_list.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/pages/anime_list.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -421,6 +421,12 @@
 	tab_bar = new QTabBar(this);
 	tab_bar->setExpanding(false);
 	tab_bar->setDrawBase(false);
+	tab_bar->setAutoFillBackground(true);
+
+	if (parent) {
+		QPalette pal(parent->palette());
+		setPalette(pal);
+	}
 
 	/* Tree view... */
 	QWidget* tree_widget = new QWidget(this);
--- a/src/gui/pages/now_playing.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/pages/now_playing.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -93,11 +93,7 @@
 	if (_id == anime.GetId() && std::to_string(_episode) == info.at("episode"))
 		return;
 	_id = anime.GetId();
-	try {
-		_episode = std::stoi(info.at("episode"));
-	} catch (std::invalid_argument const&) {
-		_episode = 0;
-	}
+	_episode = Strings::ToInt(info.at("episode"));
 	_title->SetText(Strings::ToQString(anime.GetUserPreferredTitle()));
 	_info->SetAnime(anime);
 	_poster->SetAnime(anime);
--- a/src/gui/pages/statistics.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/pages/statistics.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -40,8 +40,13 @@
 	QVBoxLayout* content_layout = new QVBoxLayout(content);
 	_score_distribution_graph.reset(new Graph<int>(content));
 	content_layout->addWidget(_score_distribution_graph.get());
-	content_layout->setContentsMargins(0, 0, 0, 0);
-	content->setContentsMargins(12, 0, 0, 0);
+	content_layout->setSpacing(0);
+	/* For some reason, when we set the margin to 12 on any paragraphs it 
+	   actually doesn't draw them 12 pixels away. It draws them ~15 pixels
+	   away! I'm assuming this is just Qt's widgets being weird (they usually are)
+	   and I hope it's nothing I *really* have to worry about... */
+	content_layout->setContentsMargins(15, 0, 0, 0);
+	content->setContentsMargins(0, 0, 0, 0);
 
 	score_dist_layout->addWidget(content);
 	score_dist_layout->setContentsMargins(0, 0, 0, 0);
@@ -69,7 +74,7 @@
 #define ADD_TIME_SEGMENT(r, x, s, p) \
 	if (x > 0) \
 	r << x << ((x == 1) ? s : p)
-std::string StatisticsPage::MinutesToDateString(int minutes) {
+std::string StatisticsPage::MinutesToDateString(const int minutes) {
 	/* ew */
 	int years = (minutes * (1 / 525949.2F));
 	int months = (minutes * (1 / 43829.1F)) - (years * 12);
@@ -86,7 +91,7 @@
 	return return_stream.str();
 }
 
-std::string StatisticsPage::SecondsToDateString(int sec) {
+std::string StatisticsPage::SecondsToDateString(const int sec) {
 	/* this is all fairly unnecessary, but works:tm: */
 	int years = sec * (1 / 31556952.0F);
 	int months = sec * (1 / 2629746.0F) - (years * 12);
@@ -107,7 +112,7 @@
 }
 #undef ADD_TIME_SEGMENT
 
-inline int GetTotalWithScore(int score) {
+inline int GetTotalWithScore(const int score) {
 	int count = 0;
 	for (const auto& item : Anime::db.items)
 		if (item.second.IsInUserList() && item.second.GetUserScore() == score)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/theme.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -0,0 +1,136 @@
+#include "core/config.h"
+#include "core/session.h"
+#include <QApplication>
+#include <QFile>
+#include <QDebug>
+#include <QTextStream>
+#include <QStyleFactory>
+#ifdef MACOSX
+#	include "sys/osx/dark_theme.h"
+#else
+#	include "sys/win32/dark_theme.h"
+#endif
+
+/* This is, believe it or not, one of the hardest things I've implemented :/
+     1. Dark mode stuff in Qt changes a lot and Qt 5 and Qt 6 are massively different
+     2. Some widgets, i.e. QTabWidget, QTabBar, etc., just completely IGNORE the QPalette setting
+     3. I don't want to use the Fusion style on every single platform
+     4. Windows dark mode support in Qt 6.5 (with Fusion) is completely unavoidable
+        (not a joke btw, it's retarded)
+   These three already make it really hard, but along with that, I don't even remember if
+   OS X dark mode support even works still; I remember the background of some of the widgets
+   would refuse to update for whatever reason. */
+
+namespace Theme {
+
+Theme::Theme(Themes theme) {
+	this->theme = theme;
+}
+
+Themes Theme::GetTheme() {
+	return theme;
+}
+
+bool Theme::IsInDarkMode() {
+	if (theme != Themes::OS)
+		return (theme == Themes::DARK);
+#ifdef MACOSX
+	if (osx::DarkThemeAvailable())
+		return osx::IsInDarkTheme();
+#elif defined(WIN32)
+	if (win32::DarkThemeAvailable())
+		return win32::IsInDarkTheme();
+#endif
+	return (theme == Themes::DARK);
+}
+
+void Theme::SetToDarkTheme() {
+	/* macOS >= 10.14 has its own global dark theme,
+	   use it :) */
+#if MACOSX
+	if (osx::DarkThemeAvailable())
+		osx::SetToDarkTheme();
+	else
+#endif
+		SetStyleSheet(Themes::DARK);
+}
+
+void Theme::SetToLightTheme() {
+#if MACOSX
+	if (osx::DarkThemeAvailable())
+		osx::SetToLightTheme();
+	else
+#endif
+		SetStyleSheet(Themes::LIGHT);
+}
+
+Themes Theme::GetCurrentOSTheme() {
+#if MACOSX
+	if (osx::DarkThemeAvailable())
+		return osx::IsInDarkTheme() ? Themes::DARK : Themes::LIGHT;
+#elif defined(WIN32)
+	if (win32::DarkThemeAvailable())
+		return win32::IsInDarkTheme() ? Themes::DARK : Themes::LIGHT;
+#endif
+	/* Currently OS detection only supports Windows and macOS.
+	   Please don't be shy if you're willing to port it to other OSes
+	   (or desktop environments, or window managers) */
+	return Themes::LIGHT;
+}
+
+/* this function is private, and should stay that way */
+void Theme::SetStyleSheet(Themes theme) {
+	switch (theme) {
+		case Themes::DARK: {
+			QColor darkGray(53, 53, 53);
+			QColor gray(128, 128, 128);
+			QColor black(25, 25, 25);
+			QColor blue(42, 130, 218);
+
+			QPalette darkPalette;
+			darkPalette.setColor(QPalette::Window, darkGray);
+			darkPalette.setColor(QPalette::WindowText, Qt::white);
+			darkPalette.setColor(QPalette::Base, black);
+			darkPalette.setColor(QPalette::AlternateBase, darkGray);
+			darkPalette.setColor(QPalette::ToolTipBase, blue);
+			darkPalette.setColor(QPalette::ToolTipText, Qt::white);
+			darkPalette.setColor(QPalette::Text, Qt::white);
+			darkPalette.setColor(QPalette::Button, darkGray);
+			darkPalette.setColor(QPalette::ButtonText, Qt::white);
+			darkPalette.setColor(QPalette::Link, blue);
+			darkPalette.setColor(QPalette::Highlight, blue);
+			darkPalette.setColor(QPalette::HighlightedText, Qt::black);
+
+			darkPalette.setColor(QPalette::Active, QPalette::Button, gray.darker());
+			darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
+			darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
+			darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
+			darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
+			qApp->setPalette(darkPalette);
+			break;
+		}
+		default:
+			qApp->setPalette(QApplication::style()->standardPalette());
+			break;
+	}
+}
+
+void Theme::SetTheme(Themes theme) {
+	switch (theme) {
+		case Themes::LIGHT:
+			SetToLightTheme();
+			break;
+		case Themes::DARK:
+			SetToDarkTheme();
+			break;
+		case Themes::OS:
+			if (GetCurrentOSTheme() == Themes::LIGHT)
+				SetToLightTheme();
+			else
+				SetToDarkTheme();
+			break;
+	}
+	this->theme = theme;
+}
+
+} // namespace DarkTheme
--- a/src/gui/widgets/text.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/widgets/text.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -197,8 +197,8 @@
 
 	content_layout->addWidget(paragraph);
 	content_layout->setSpacing(0);
-	content_layout->setContentsMargins(0, 0, 0, 0);
-	content->setContentsMargins(12, 0, 0, 0);
+	content_layout->setContentsMargins(12, 0, 0, 0);
+	content->setContentsMargins(0, 0, 0, 0);
 
 	layout->addWidget(header);
 	layout->addWidget(content);
--- a/src/gui/window.cc	Fri Nov 03 14:06:02 2023 -0400
+++ b/src/gui/window.cc	Fri Nov 03 21:32:52 2023 -0400
@@ -3,7 +3,7 @@
 #include "core/config.h"
 #include "core/session.h"
 #include "core/strings.h"
-#include "gui/dark_theme.h"
+#include "gui/theme.h"
 #include "gui/dialog/about.h"
 #include "gui/dialog/settings.h"
 #include "gui/pages/anime_list.h"
@@ -106,8 +106,6 @@
 		page->SetPlaying(Anime::db.items[id], elements);
 	});
 	timer->start(5000);
-
-	DarkTheme::SetTheme(session.config.theme);
 }
 
 void MainWindow::CreateBars() {