1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#include "darkmode.h"
#include <zen/sys_version.h>
#include <wx/settings.h>
#include "color_tools.h"
#include <gtk/gtk.h>
using namespace zen;
bool zen::darkModeAvailable()
{
#if GTK_MAJOR_VERSION == 2
return false;
#elif GTK_MAJOR_VERSION >= 3
return true;
#else
#error unknown GTK version!
#endif
}
namespace
{
class SysColorsHook : public wxColorHook
{
public:
wxColor getColor(wxSystemColour index) const override
{
//fix contrast e.g. Ubuntu's Adwaita-Dark theme and macOS dark mode:
if (index == wxSYS_COLOUR_GRAYTEXT)
return colGreyTextEnhContrast_;
#if 0
auto colToString = [](wxColor c)
{
const auto& [rh, rl] = hexify(c.Red ());
const auto& [gh, gl] = hexify(c.Green());
const auto& [bh, bl] = hexify(c.Blue ());
const auto& [ah, al] = hexify(c.Alpha());
return "#" + std::string({rh, rl, gh, gl, bh, bl, ah, al});
};
std::cerr << "wxSYS_COLOUR_GRAYTEXT " << colToString(wxSystemSettingsNative::GetColour(wxSYS_COLOUR_GRAYTEXT)) << "\n";
#endif
return wxSystemSettingsNative::GetColour(index); //fallback
}
private:
const wxColor colGreyTextEnhContrast_ =
enhanceContrast(wxSystemSettingsNative::GetColour(wxSYS_COLOUR_GRAYTEXT),
wxSystemSettingsNative::GetColour(wxSYS_COLOUR_WINDOWTEXT),
wxSystemSettingsNative::GetColour(wxSYS_COLOUR_WINDOW), 4.5 /*contrastRatioMin*/); //W3C recommends >= 4.5
};
std::optional<bool> globalDefaultThemeIsDark;
}
void zen::colorThemeInit(wxApp& app, ColorTheme colTheme) //throw FileError
{
assert(!refGlobalColorHook());
globalDefaultThemeIsDark = wxSystemSettings::GetAppearance().AreAppsDark();
ZEN_ON_SCOPE_EXIT(if (!refGlobalColorHook()) refGlobalColorHook() = std::make_unique<SysColorsHook>()); //*after* SetAppearance() and despite errors
//caveat: on macOS there are more themes than light/dark: https://developer.apple.com/documentation/appkit/nsappearance/name-swift.struct
if (colTheme != ColorTheme::System && //"System" is already the default for macOS/Linux(GTK3)
darkModeAvailable())
changeColorTheme(colTheme); //throw FileError
}
void zen::colorThemeCleanup()
{
assert(refGlobalColorHook());
refGlobalColorHook().reset();
}
bool zen::equalAppearance(ColorTheme colTheme1, ColorTheme colTheme2)
{
if (colTheme1 == ColorTheme::System) colTheme1 = *globalDefaultThemeIsDark ? ColorTheme::Dark : ColorTheme::Light;
if (colTheme2 == ColorTheme::System) colTheme2 = *globalDefaultThemeIsDark ? ColorTheme::Dark : ColorTheme::Light;
return colTheme1 == colTheme2;
}
void zen::changeColorTheme(ColorTheme colTheme) //throw FileError
{
if (colTheme == ColorTheme::System) //SetAppearance(System) isn't working reliably! surprise!?
colTheme = *globalDefaultThemeIsDark ? ColorTheme::Dark : ColorTheme::Light;
try
{
ZEN_ON_SCOPE_SUCCESS(refGlobalColorHook() = std::make_unique<SysColorsHook>()); //*after* SetAppearance()
if (wxApp::AppearanceResult rv = wxTheApp->SetAppearance(colTheme);
rv != wxApp::AppearanceResult::Ok)
throw SysError(formatSystemError("wxApp::SetAppearance",
rv == wxApp::AppearanceResult::CannotChange ? L"CannotChange" : L"Failure", L"" /*errorMsg*/));
}
catch (const SysError& e) { throw FileError(_("Failed to update the color theme."), e.toString()); }
}
|