changeset 105:6d8da6e64d61

theme: add dark stylesheet, make it actually usable win32: make the titlebar black where available
author Paper <mrpapersonic@gmail.com>
date Sun, 05 Nov 2023 03:54:26 -0500
parents 27455104ea37
children c8c72278f6fd
files CMakeLists.txt include/core/strings.h include/gui/theme.h include/gui/window.h include/sys/win32/dark_theme.h rc/dark.qrc rc/dark.qss rc/icons.qrc src/gui/dialog/about.cc src/gui/dialog/information.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 src/sys/osx/dark_theme.mm src/sys/win32/dark_theme.cc
diffstat 19 files changed, 520 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Sun Nov 05 02:35:27 2023 -0500
+++ b/CMakeLists.txt	Sun Nov 05 03:54:26 2023 -0500
@@ -102,6 +102,7 @@
 
 	# Qt resources
 	rc/icons.qrc
+	rc/dark.qrc
 )
 
 set(RC_INFO_STRING "A lightweight anime tracker built with Qt.")
--- a/include/core/strings.h	Sun Nov 05 02:35:27 2023 -0500
+++ b/include/core/strings.h	Sun Nov 05 03:54:26 2023 -0500
@@ -3,11 +3,73 @@
 
 #include <string>
 #include <vector>
+#include <array>
 
 class QString;
 
 namespace Strings {
 
+template<unsigned...>struct seq{using type=seq;};
+template<unsigned N, unsigned... Is>
+struct gen_seq_x : gen_seq_x<N-1, N-1, Is...>{};
+template<unsigned... Is>
+struct gen_seq_x<0, Is...> : seq<Is...>{};
+template<unsigned N>
+using gen_seq=typename gen_seq_x<N>::type;
+
+template<size_t S>
+using size=std::integral_constant<size_t, S>;
+
+template<class T, size_t N>
+constexpr size<N> length( T const(&)[N] ) { return {}; }
+template<class T, size_t N>
+constexpr size<N> length( std::array<T, N> const& ) { return {}; }
+
+template<class T>
+using length_t = decltype(length(std::declval<T>()));
+
+constexpr size_t string_size() { return 0; }
+template<class...Ts>
+constexpr size_t string_size( size_t i, Ts... ts ) {
+  return (i?i-1:0) + string_size(ts...);
+}
+template<class...Ts>
+using string_length=size< string_size( length_t<Ts>{}... )>;
+
+template<class...Ts>
+using combined_string = std::array<char, string_length<Ts...>{}+1>;
+
+template<class Lhs, class Rhs, unsigned...I1, unsigned...I2>
+constexpr const combined_string<Lhs,Rhs>
+concat_impl( Lhs const& lhs, Rhs const& rhs, seq<I1...>, seq<I2...>)
+{
+    return {{ lhs[I1]..., rhs[I2]..., '\0' }};
+}
+
+template<class Lhs, class Rhs>
+constexpr const combined_string<Lhs,Rhs>
+concat(Lhs const& lhs, Rhs const& rhs)
+{
+    return concat_impl(lhs, rhs, gen_seq<string_length<Lhs>{}>{}, gen_seq<string_length<Rhs>{}>{});
+}
+
+template<class T0, class T1, class... Ts>
+constexpr const combined_string<T0, T1, Ts...>
+concat(T0 const&t0, T1 const&t1, Ts const&...ts)
+{
+    return concat(t0, concat(t1, ts...));
+}
+
+template<class T>
+constexpr const combined_string<T>
+concat(T const&t) {
+    return concat(t, "");
+}
+constexpr const combined_string<>
+concat() {
+    return concat("");
+}
+
 /* Implode function: takes a vector of strings and turns it
    into a string, separated by delimiters. */
 std::string Implode(const std::vector<std::string>& vector, const std::string& delimiter);
--- a/include/gui/theme.h	Sun Nov 05 02:35:27 2023 -0500
+++ b/include/gui/theme.h	Sun Nov 05 03:54:26 2023 -0500
@@ -16,9 +16,10 @@
 		Theme(Themes theme = Themes::OS);
 		void SetTheme(Themes theme);
 		Themes GetTheme();
+		bool IsInDarkMode();
+		void RepaintCurrentTheme();
 
 	private:
-		bool IsInDarkMode();
 		void SetToDarkTheme();
 		void SetToLightTheme();
 		void SetStyleSheet(Themes theme);
--- a/include/gui/window.h	Sun Nov 05 02:35:27 2023 -0500
+++ b/include/gui/window.h	Sun Nov 05 03:54:26 2023 -0500
@@ -15,6 +15,7 @@
 		MainWindow(QWidget* parent = nullptr);
 		void SetActivePage(QWidget* page);
 		void CreateBars();
+		void showEvent(QShowEvent* event) override;
 		void closeEvent(QCloseEvent* event) override;
 
 	private:
--- a/include/sys/win32/dark_theme.h	Sun Nov 05 02:35:27 2023 -0500
+++ b/include/sys/win32/dark_theme.h	Sun Nov 05 03:54:26 2023 -0500
@@ -1,11 +1,15 @@
 #ifndef __sys__win32__dark_theme_h
 #define __sys__win32__dark_theme_h
 
+class QWidget;
+
 namespace win32 {
 
+bool SetTitleBarToBlack(QWidget* win, bool enabled);
+void SetTitleBarsToBlack(bool enabled);
 bool DarkThemeAvailable();
 bool IsInDarkTheme();
 
 } // namespace win32
 
-#endif // __sys__win32__dark_theme_h
\ No newline at end of file
+#endif // __sys__win32__dark_theme_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/dark.qrc	Sun Nov 05 03:54:26 2023 -0500
@@ -0,0 +1,5 @@
+<!DOCTYPE rcc><RCC version="1.0">
+	<qresource>
+		<file>dark.qss</file>
+	</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/dark.qss	Sun Nov 05 03:54:26 2023 -0500
@@ -0,0 +1,286 @@
+/*
+Much of this is taken from the Breeze Style Sheets,
+which is under the MIT license:
+
+Copyright © `<2013-2014>` `<Colin Duquesnoy>`
+Copyright © `<2015-2016>` `<Alex Huszagh>`
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the “Software”), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* Colors should be the same as the QPalette in gui/theme.cc, i.e.:
+	window == darkgray == #353535;
+	??? == gray == #808080;
+	base == black == #191919;
+	highlighted == blue = #2A82DA;
+
+   It's a shame Qt stylesheets don't properly support variables :')
+   Maybe we could configure it with cmake?
+*/
+
+AnimeListPage::pane {
+	margin: 0.04em;
+}
+
+AnimeListPage::pane,
+QTabWidget::pane:top {
+	border: 0.04em solid #808080;
+	top: -0.04em;
+}
+
+AnimeListPage::pane,
+QTabWidget::pane:bottom {
+	border: 0.04em solid #808080;
+	bottom: -0.04em;
+}
+
+AnimeListPage::pane,
+QTabWidget::pane:left {
+	border: 0.04em solid #808080;
+	left: -0.04em;
+}
+
+AnimeListPage::pane,
+QTabWidget::pane:right {
+	border: 0.04em solid #808080;
+	right: -0.04em;
+}
+
+QTabBar {
+	qproperty-drawBase: 0;
+	left: 0.23em;
+	border-radius: 0.13em;
+	selection-color: transparent;
+}
+
+QTabBar:focus {
+	border: 0em transparent black;
+}
+
+QTabBar::tab:top,
+QTabBar::tab:top:last,
+QTabBar::tab:top:only-one {
+	color: white;
+	border: 0.04em transparent black;
+	border-left: 0.04em solid #808080;
+	border-right: 0.04em solid #808080;
+	border-top: 0.04em solid #808080;
+	background-color: #353535;
+	min-width: 50px;
+	padding-top: 0.23em;
+	padding-bottom: 0.23em;
+	padding-left: 0.46em;
+	padding-right: 0.46em;
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:!selected {
+	color: white;
+	background-color: #353535;
+	border: 0.04em solid #808080;
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+	margin-top: 0.13em;
+}
+
+QTabBar::tab:top:next-selected {
+	border-right: 0.04em transparent #353535;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:previous-selected {
+	border-left: 0.04em transparent #353535;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:!selected:hover {
+	background-color: rgba(42, 130, 218, 0.1);
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QTabBar::tab:top:!selected:first:hover {
+	background-color: rgba(42, 130, 218, 0.1);
+	border-radius: 0.09em;
+	border-bottom-left-radius: 0em;
+	border-bottom-right-radius: 0em;
+}
+
+QGroupBox::title {
+	color: white;
+}
+
+QComboBox,
+QPushButton,
+QDateEdit,
+QSpinBox {
+	background-color: #353535;
+	color: white;
+}
+
+/*
+ * QLineEdit
+ */
+
+QLineEdit:!read-only {
+	background-color: #191919;
+	padding: 0.23em;
+	border-style: solid;
+	border: 0.04em solid #808080;
+	border-radius: 0.09em;
+}
+
+Line {
+	background: transparent;
+}
+
+/* QMenuBar */
+
+QMenuBar {
+	background-color: #353535;
+	color: white;
+}
+
+QMenuBar::item {
+	background: transparent;
+}
+
+QMenuBar::item:selected {
+	background: transparent;
+	border: 0.04em solid #2A82DA;
+}
+
+QMenuBar::item:disabled {
+	color: #808080;
+}
+
+QMenuBar::item:pressed {
+	background-color: #2A82DA;
+	color: white;
+	margin-bottom: -0.09em;
+	padding-bottom: 0.09em;
+}
+
+/* QMenu */
+
+QMenu {
+	color: white;
+	background-color: #353535;
+	margin: 0.09em;
+}
+
+QMenu::icon {
+	margin: 0.23em;
+}
+
+QMenu::item {
+	/* Add extra padding on the right for the QMenu arrow */
+	padding: 0.23em 1.5em 0.23em 1.3em;
+	border: 0.09em solid transparent;
+	background: transparent;
+}
+
+QMenu::item:selected {
+	color: white;
+	background-color: #2A82DA;
+}
+
+QMenu::item:selected:disabled {
+	background-color: #353535;
+}
+
+QMenu::item:disabled {
+	color: #808080;
+}
+
+QMenu::indicator {
+	width: 0.8em;
+	height: 0.8em;
+	/* To align with QMenu::icon, which has a 0.23em margin. */
+	margin-left: 0.3em;
+	subcontrol-position: center left;
+}
+
+/*
+ * QHeaderView:
+ * Need this for the anime list, on Windows it gets screwed up.
+*/
+
+QHeaderView {
+	background-color: #353535;
+	border: 0.04em transparent;
+	border-radius: 0em;
+	margin: 0em;
+	padding: 0em;
+}
+
+QHeaderView::section {
+	background-color: #353535;
+	border: 0.04em solid #808080;
+	color: #eff0f1;
+	border-radius: 0em;
+	padding: 0em 0.23em 0em 0.23em;
+	text-align: center;
+}
+
+QHeaderView::section::vertical::first,
+QHeaderView::section::vertical::only-one {
+	border-top: 0.04em solid #808080;
+}
+
+QHeaderView::section::vertical {
+	border-top: transparent;
+}
+
+QHeaderView::section::horizontal::first,
+QHeaderView::section::horizontal::only-one {
+	border-left: 0.04em solid #808080;
+}
+
+QHeaderView::section::horizontal {
+	border-left: transparent;
+}
+
+QHeaderView[showSortIndicator="true"]::section::horizontal {
+	/* Same as the width of the arrow subcontrols below. */
+	padding-right: 0.8em;
+}
+
+QHeaderView::section:checked {
+	color: white;
+	background-color: #2A82DA;
+}
+
+/* Note that this doesn't work for QTreeView unless the header is clickable */
+QHeaderView::section:hover,
+QHeaderView::section::horizontal::first:hover,
+QHeaderView::section::horizontal::only-one:hover,
+QHeaderView::section::vertical::first:hover,
+QHeaderView::section::vertical::only-one:hover {
+	border: 0.04em solid #2A82DA;
+}
--- a/rc/icons.qrc	Sun Nov 05 02:35:27 2023 -0500
+++ b/rc/icons.qrc	Sun Nov 05 03:54:26 2023 -0500
@@ -19,4 +19,4 @@
 		<file>icons/24x24/megaphone.png</file>
 		<file>icons/24x24/question.png</file>
 	</qresource>
-</RCC>
\ No newline at end of file
+</RCC>
--- a/src/gui/dialog/about.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/dialog/about.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -67,6 +67,7 @@
 	"    <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>";
 
--- a/src/gui/dialog/information.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/dialog/information.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -42,7 +42,7 @@
 	setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
 
 	{
-		QPalette pal(palette());
+		QPalette pal;
 		pal.setColor(QPalette::Window, pal.color(QPalette::Base));
 		setPalette(pal);
 	}
@@ -73,12 +73,6 @@
 	/* main info tab */
 	AnimeInfoWidget* main_information_widget = new AnimeInfoWidget(anime, tabbed_widget);
 
-	{
-		QPalette pal(main_information_widget->palette());
-		pal.setColor(QPalette::Base, pal.color(QPalette::Window));
-		main_information_widget->setPalette(pal);
-	}
-
 	QWidget* settings_widget = new QWidget(tabbed_widget);
 	settings_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
 
--- a/src/gui/dialog/settings/application.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/dialog/settings/application.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -12,6 +12,7 @@
 
 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);
--- a/src/gui/pages/anime_list.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/pages/anime_list.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -282,14 +282,13 @@
 	}
 
 	menu->addSeparator();
-	QAction* resetAction = menu->addAction(tr("Reset to defaults"), this, [this]() {
+	menu->addAction(tr("Reset to defaults"), this, [this]() {
 		for (int i = 0, count = tree_view->header()->count(); i < count; ++i) {
 			SetColumnDefaults();
 		}
 		// SaveSettings();
 	});
 	menu->popup(QCursor::pos());
-	(void)(resetAction);
 }
 
 void AnimeListPage::DisplayListMenu() {
@@ -312,7 +311,7 @@
 			animes.insert(anime);
 	}
 
-	QAction* action = menu->addAction(tr("Information"), [this, animes] {
+	menu->addAction(tr("Information"), [this, animes] {
 		for (auto& anime : animes) {
 			InformationDialog* dialog = new InformationDialog(
 			    *anime, [this, anime] { UpdateAnime(anime->GetId()); }, this);
@@ -323,7 +322,7 @@
 		}
 	});
 	menu->addSeparator();
-	action = menu->addAction(tr("Delete from list..."), [this, animes] {
+	menu->addAction(tr("Delete from list..."), [this, animes] {
 		for (auto& anime : animes) {
 			RemoveAnime(anime->GetId());
 		}
@@ -387,7 +386,6 @@
 
 	InitBasicStyle(option);
 
-	// int exth = style()->pixelMetric(QStyle::PM_TabBarBaseHeight, nullptr, this);
 	QSize t(0, tree_view->frameWidth());
 	if (tab_bar->isVisibleTo(this)) {
 		t = tab_bar->sizeHint();
@@ -421,12 +419,6 @@
 	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	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/pages/now_playing.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -108,10 +108,6 @@
 
 	setFrameShape(QFrame::Box);
 	setFrameShadow(QFrame::Sunken);
-
-	QPalette pal = QPalette();
-	pal.setColor(QPalette::Window, pal.color(QPalette::Base));
-	setPalette(pal);
 	setAutoFillBackground(true);
 
 	stack = new QStackedWidget(this);
--- a/src/gui/pages/statistics.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/pages/statistics.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -18,9 +18,6 @@
 	setFrameShape(QFrame::Box);
 	setFrameShadow(QFrame::Sunken);
 
-	QPalette pal = QPalette();
-	pal.setColor(QPalette::Window, pal.color(QPalette::Base));
-	setPalette(pal);
 	setAutoFillBackground(true);
 
 	_anime_list.reset(new TextWidgets::LabelledSection(
--- a/src/gui/theme.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/theme.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -7,7 +7,7 @@
 #include <QStyleFactory>
 #ifdef MACOSX
 #	include "sys/osx/dark_theme.h"
-#else
+#elif WIN32
 #	include "sys/win32/dark_theme.h"
 #endif
 
@@ -47,25 +47,31 @@
 void Theme::SetToDarkTheme() {
 	/* macOS >= 10.14 has its own global dark theme,
 	   use it :) */
-#if MACOSX
+#ifdef MACOSX
 	if (osx::DarkThemeAvailable())
 		osx::SetToDarkTheme();
 	else
+#elif defined(WIN32)
+	if (win32::DarkThemeAvailable())
+		win32::SetTitleBarsToBlack(true);
 #endif
 		SetStyleSheet(Themes::DARK);
 }
 
 void Theme::SetToLightTheme() {
-#if MACOSX
+#ifdef MACOSX
 	if (osx::DarkThemeAvailable())
 		osx::SetToLightTheme();
 	else
+#elif defined(WIN32)
+	if (win32::DarkThemeAvailable())
+		win32::SetTitleBarsToBlack(false);
 #endif
 		SetStyleSheet(Themes::LIGHT);
 }
 
 Themes Theme::GetCurrentOSTheme() {
-#if MACOSX
+#ifdef MACOSX
 	if (osx::DarkThemeAvailable())
 		return osx::IsInDarkTheme() ? Themes::DARK : Themes::LIGHT;
 #elif defined(WIN32)
@@ -87,30 +93,47 @@
 			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);
+			QPalette pal(QApplication::style()->standardPalette());
+			pal.setColor(QPalette::Window, darkGray);
+			pal.setColor(QPalette::WindowText, Qt::white);
+			pal.setColor(QPalette::Base, black);
+			pal.setColor(QPalette::AlternateBase, darkGray);
+			pal.setColor(QPalette::ToolTipBase, blue);
+			pal.setColor(QPalette::ToolTipText, Qt::white);
+			pal.setColor(QPalette::Text, Qt::white);
+			pal.setColor(QPalette::Button, darkGray);
+			pal.setColor(QPalette::ButtonText, Qt::white);
+			pal.setColor(QPalette::Link, blue);
+			pal.setColor(QPalette::Highlight, blue);
+			pal.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);
+			pal.setColor(QPalette::Active, QPalette::Button, gray.darker());
+			pal.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
+			pal.setColor(QPalette::Disabled, QPalette::WindowText, gray);
+			pal.setColor(QPalette::Disabled, QPalette::Text, gray);
+			pal.setColor(QPalette::Disabled, QPalette::Light, darkGray);
+			qApp->setPalette(pal);
+
+			QFile f(":dark.qss");
+			if (!f.exists())
+				break; // how?
+			f.open(QFile::ReadOnly | QFile::Text);
+			QTextStream ts(&f);
+			qApp->setStyleSheet(ts.readAll());
 			break;
 		}
 		default:
-			qApp->setPalette(QApplication::style()->standardPalette());
+			QPalette pal(QApplication::style()->standardPalette());
+#ifdef WIN32 /* fuck you Qt 6 */
+			pal.setColor(QPalette::Window, QColor(0xF0, 0xF0, 0xF0));
+#endif
+			pal.setColor(QPalette::WindowText, Qt::black);
+			pal.setColor(QPalette::ToolTipText, Qt::black);
+			pal.setColor(QPalette::Text, Qt::black);
+			pal.setColor(QPalette::ButtonText, Qt::black);
+			qApp->setPalette(pal);
+
+			qApp->setStyleSheet("");
 			break;
 	}
 }
@@ -133,4 +156,8 @@
 	this->theme = theme;
 }
 
+void Theme::RepaintCurrentTheme() {
+	Theme::SetTheme(theme);
+}
+
 } // namespace DarkTheme
--- a/src/gui/widgets/text.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/widgets/text.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -80,6 +80,7 @@
 	setCursor(Qt::IBeamCursor);
 
 	QPalette pal;
+	pal.setColor(QPalette::Window, Qt::transparent);
 	pal.setColor(QPalette::Base, Qt::transparent);
 	setPalette(pal);
 
@@ -101,7 +102,7 @@
 	setFont(fnt);
 
 	QPalette pal(palette());
-	pal.setColor(QPalette::Text, Qt::blue);
+	pal.setColor(QPalette::Text, pal.color(QPalette::Highlight));
 	setPalette(pal);
 }
 
--- a/src/gui/window.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/gui/window.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -272,6 +272,13 @@
 	this->setCentralWidget(page);
 }
 
+void MainWindow::showEvent(QShowEvent* event) {
+	QMainWindow::showEvent(event);
+#ifdef WIN32
+	win32::SetTitleBarToBlack(this, session.config.theme.IsInDarkMode());
+#endif
+}
+
 void MainWindow::closeEvent(QCloseEvent* event) {
 	session.config.Save();
 	event->accept();
--- a/src/sys/osx/dark_theme.mm	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/sys/osx/dark_theme.mm	Sun Nov 05 03:54:26 2023 -0500
@@ -3,6 +3,8 @@
 
 namespace osx {
 
+/* I remember clang giving a hissy fit when I tried simplifying this to just
+   a return; does it still do that? */
 bool DarkThemeAvailable() {
 	if (@available(macOS 10.14, *))
 		return true;
@@ -28,7 +30,7 @@
 
 void SetToLightTheme() {
 	// https://stackoverflow.com/questions/55925862/how-can-i-set-my-os-x-application-theme-in-code
-	if (__builtin_available(macOS 10.14, *)) {
+	if (@available(macOS 10.14, *)) {
 		[NSApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]];
 	}
 }
--- a/src/sys/win32/dark_theme.cc	Sun Nov 05 02:35:27 2023 -0500
+++ b/src/sys/win32/dark_theme.cc	Sun Nov 05 03:54:26 2023 -0500
@@ -1,12 +1,98 @@
 #include "sys/win32/dark_theme.h"
+#include <QApplication>
+#include <QDebug>
 #include <QOperatingSystemVersion>
 #include <QSettings>
+#include <QWidget>
+#include <iostream>
+#include <dwmapi.h>
+
+/* let's make a class wrapper around HINSTANCE, 
+   so we don't fuck anything up :). */
+class Library {
+	public:
+		Library() {}
+		~Library() {
+			Unload();
+		}
+		void Unload() {
+			if (hInstance)
+				FreeLibrary(hInstance);
+			loaded = false;
+		}
+		void Load(LPCWSTR name) {
+			if (loaded)
+				Unload();
+			hInstance = LoadLibraryW(name);
+			if (hInstance)
+				loaded = true;
+		}
+		HINSTANCE GetInstance() {
+			return hInstance;
+		}
+		bool IsLoaded() {
+			return loaded;
+		}
+	private:
+		HINSTANCE hInstance = nullptr;
+		bool loaded = false;
+};
+
+Library dwmapi;
 
 namespace win32 {
 
+#define GET_FUNCTION(f, i) \
+	reinterpret_cast<decltype(::f)*>(GetProcAddress(i, #f))
+
+static HRESULT SetWindowAttribute(HWND hWnd, DWORD key, LPCVOID data, DWORD sz_data) {
+	if (!dwmapi.IsLoaded()) {
+		dwmapi.Load(L"dwmapi.dll");
+		if (!dwmapi.IsLoaded())
+			return false;
+	}
+
+	HINSTANCE hInstance = dwmapi.GetInstance();
+	if (!hInstance)
+		return false;
+
+	auto set_wind_attrib = GET_FUNCTION(DwmSetWindowAttribute, hInstance);
+	if (!set_wind_attrib)
+		return false;
+
+	return set_wind_attrib(hWnd, key, data, sz_data);
+}
+
+/* Ok, so this doesn't work. Woohoo! :) */
+bool SetTitleBarToBlack(QWidget* win, bool enabled) {
+	BOOL b = enabled;
+
+	/* MAGIC NUMBERS: 19 and 20 are both DWMWA_USE_IMMERSIVE_DARK_MODE.
+	   clarification: it's 20 on newer versions of windows (i.e. win11 and late win10),
+	   but it's 19 on very old versions of win10 nobody ought to be using. */
+	{
+		HRESULT result = SetWindowAttribute(reinterpret_cast<HWND>(win->winId()), 20, &b, sizeof(b));
+		if (result == S_OK)
+			return b;
+	}
+
+	{
+		/* It's 19 in older Windows 10 versions. Yeah, stupid. */
+		HRESULT result = SetWindowAttribute(reinterpret_cast<HWND>(win->winId()), 19, &b, sizeof(b));
+		if (result == S_OK)
+			return b;
+	}
+
+	return b;
+}
+
+void SetTitleBarsToBlack(bool enabled) {
+	for (QWidget* widget : qApp->topLevelWidgets()) {
+		SetTitleBarToBlack(widget, enabled);
+	}
+}
+
 bool DarkThemeAvailable() {
-	// dark mode supported Windows 10 1809 10.0.17763 onward
-	// https://stackoverflow.com/questions/53501268/win10-dark-theme-how-to-use-in-winapi
 	const auto& ver = QOperatingSystemVersion::current();
 	return (ver.majorVersion() > 10) ? true : (ver.majorVersion() == 10 && ver.microVersion() >= 17763);
 }