mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Implement system-based dark mode for Windows and Linux
This commit is contained in:
parent
fc3a9d98c0
commit
47a237c924
12 changed files with 185 additions and 20 deletions
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/local_url_handlers.h"
|
||||
#include "core/launcher.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "chat_helpers/emoji_keywords.h"
|
||||
#include "chat_helpers/stickers_emoji_image_loader.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
|
@ -213,6 +214,23 @@ void Application::run() {
|
|||
startEmojiImageLoader();
|
||||
Media::Player::start(_audio.get());
|
||||
|
||||
const auto darkModeChanged = [] {
|
||||
const auto darkMode = Core::App().settings().systemDarkMode();
|
||||
const auto darkModeEnabled = Core::App().settings().systemDarkModeEnabled();
|
||||
if (darkModeEnabled
|
||||
&& darkMode.has_value()
|
||||
&& (*darkMode != Window::Theme::IsNightMode())) {
|
||||
Window::Theme::ToggleNightMode();
|
||||
Window::Theme::KeepApplied();
|
||||
}
|
||||
};
|
||||
|
||||
Core::App().settings().systemDarkModeChanges(
|
||||
) | rpl::start_with_next(darkModeChanged, _lifetime);
|
||||
|
||||
Core::App().settings().systemDarkModeEnabledChanges(
|
||||
) | rpl::start_with_next(darkModeChanged, _lifetime);
|
||||
|
||||
style::ShortAnimationPlaying(
|
||||
) | rpl::start_with_next([=](bool playing) {
|
||||
if (playing) {
|
||||
|
@ -235,6 +253,7 @@ void Application::run() {
|
|||
_domain->activeChanges(
|
||||
) | rpl::start_with_next([=](not_null<Main::Account*> account) {
|
||||
_window->showAccount(account);
|
||||
Core::App().settings().setSystemDarkMode(Platform::IsDarkMode());
|
||||
}, _window->widget()->lifetime());
|
||||
|
||||
QCoreApplication::instance()->installEventFilter(this);
|
||||
|
|
|
@ -106,7 +106,8 @@ QByteArray Settings::serialize() const {
|
|||
<< qint32(_thirdColumnWidth.current())
|
||||
<< qint32(_thirdSectionExtendedBy)
|
||||
<< qint32(_notifyFromAll ? 1 : 0)
|
||||
<< qint32(_nativeWindowFrame.current() ? 1 : 0);
|
||||
<< qint32(_nativeWindowFrame.current() ? 1 : 0)
|
||||
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -171,6 +172,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
|||
qint32 thirdSectionExtendedBy = _thirdSectionExtendedBy;
|
||||
qint32 notifyFromAll = _notifyFromAll ? 1 : 0;
|
||||
qint32 nativeWindowFrame = _nativeWindowFrame.current() ? 1 : 0;
|
||||
qint32 systemDarkModeEnabled = _systemDarkModeEnabled.current() ? 1 : 0;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (!stream.atEnd()) {
|
||||
|
@ -248,6 +250,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
|||
if (!stream.atEnd()) {
|
||||
stream >> nativeWindowFrame;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> systemDarkModeEnabled;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
|
@ -341,6 +346,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
|||
}
|
||||
_notifyFromAll = (notifyFromAll == 1);
|
||||
_nativeWindowFrame = (nativeWindowFrame == 1);
|
||||
_systemDarkModeEnabled = (systemDarkModeEnabled == 1);
|
||||
}
|
||||
|
||||
bool Settings::chatWide() const {
|
||||
|
@ -474,6 +480,7 @@ void Settings::resetOnLastLogout() {
|
|||
_thirdColumnWidth = kDefaultThirdColumnWidth; // p-w
|
||||
_notifyFromAll = true;
|
||||
_tabbedReplacedWithInfo = false; // per-window
|
||||
_systemDarkModeEnabled = false;
|
||||
}
|
||||
|
||||
bool Settings::ThirdColumnByDefault() {
|
||||
|
|
|
@ -416,6 +416,30 @@ public:
|
|||
[[nodiscard]] rpl::producer<bool> nativeWindowFrameChanges() const {
|
||||
return _nativeWindowFrame.changes();
|
||||
}
|
||||
void setSystemDarkMode(std::optional<bool> value) {
|
||||
_systemDarkMode = value;
|
||||
}
|
||||
[[nodiscard]] std::optional<bool> systemDarkMode() const {
|
||||
return _systemDarkMode.current();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<std::optional<bool>> systemDarkModeValue() const {
|
||||
return _systemDarkMode.value();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<std::optional<bool>> systemDarkModeChanges() const {
|
||||
return _systemDarkMode.changes();
|
||||
}
|
||||
void setSystemDarkModeEnabled(bool value) {
|
||||
_systemDarkModeEnabled = value;
|
||||
}
|
||||
[[nodiscard]] bool systemDarkModeEnabled() const {
|
||||
return _systemDarkModeEnabled.current();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<bool> systemDarkModeEnabledValue() const {
|
||||
return _systemDarkModeEnabled.value();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<bool> systemDarkModeEnabledChanges() const {
|
||||
return _systemDarkModeEnabled.changes();
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool ThirdColumnByDefault();
|
||||
[[nodiscard]] float64 DefaultDialogsWidthRatio();
|
||||
|
@ -484,6 +508,8 @@ private:
|
|||
rpl::variable<int> _thirdColumnWidth = kDefaultThirdColumnWidth; // p-w
|
||||
bool _notifyFromAll = true;
|
||||
rpl::variable<bool> _nativeWindowFrame = false;
|
||||
rpl::variable<std::optional<bool>> _systemDarkMode = std::nullopt;
|
||||
rpl::variable<bool> _systemDarkModeEnabled = false;
|
||||
|
||||
bool _tabbedReplacedWithInfo = false; // per-window
|
||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "platform/linux/linux_desktop_environment.h"
|
||||
#include "platform/linux/specific_linux.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/application.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "mainwindow.h"
|
||||
|
@ -22,6 +23,7 @@ namespace Libs {
|
|||
namespace {
|
||||
|
||||
bool gtkTriedToInit = false;
|
||||
bool gtkLoaded = false;
|
||||
|
||||
bool loadLibrary(QLibrary &lib, const char *name, int version) {
|
||||
#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY
|
||||
|
@ -44,21 +46,6 @@ bool loadLibrary(QLibrary &lib, const char *name, int version) {
|
|||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
template <typename T>
|
||||
T gtkSetting(const gchar *propertyName) {
|
||||
GtkSettings *settings = gtk_settings_get_default();
|
||||
T value;
|
||||
g_object_get(settings, propertyName, &value, nullptr);
|
||||
return value;
|
||||
}
|
||||
|
||||
QString gtkSetting(const gchar *propertyName) {
|
||||
gchararray value = gtkSetting<gchararray>(propertyName);
|
||||
QString str = QString::fromUtf8(value);
|
||||
g_free(value);
|
||||
return str;
|
||||
}
|
||||
|
||||
void gtkMessageHandler(
|
||||
const gchar *log_domain,
|
||||
GLogLevelFlags log_level,
|
||||
|
@ -75,6 +62,7 @@ void gtkMessageHandler(
|
|||
|
||||
bool setupGtkBase(QLibrary &lib_gtk) {
|
||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_init_check", gtk_init_check)) return false;
|
||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_check_version", gtk_check_version)) return false;
|
||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_settings_get_default", gtk_settings_get_default)) return false;
|
||||
|
||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false;
|
||||
|
@ -173,10 +161,12 @@ bool IconThemeShouldBeSet() {
|
|||
|
||||
void SetIconTheme() {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||
if (IconThemeShouldBeSet()) {
|
||||
if (GtkSettingSupported()
|
||||
&& GtkLoaded()
|
||||
&& IconThemeShouldBeSet()) {
|
||||
DEBUG_LOG(("Set GTK icon theme"));
|
||||
QIcon::setThemeName(gtkSetting("gtk-icon-theme-name"));
|
||||
QIcon::setFallbackThemeName(gtkSetting("gtk-fallback-icon-theme"));
|
||||
QIcon::setThemeName(GtkSetting("gtk-icon-theme-name"));
|
||||
QIcon::setFallbackThemeName(GtkSetting("gtk-fallback-icon-theme"));
|
||||
Platform::SetApplicationIcon(Window::CreateIcon());
|
||||
if (App::wnd()) {
|
||||
App::wnd()->setWindowIcon(Window::CreateIcon());
|
||||
|
@ -185,12 +175,19 @@ void SetIconTheme() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DarkModeChanged() {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||
Core::App().settings().setSystemDarkMode(Platform::IsDarkMode());
|
||||
});
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
f_gtk_init_check gtk_init_check = nullptr;
|
||||
f_gtk_check_version gtk_check_version = nullptr;
|
||||
f_gtk_settings_get_default gtk_settings_get_default = nullptr;
|
||||
f_gtk_widget_show gtk_widget_show = nullptr;
|
||||
f_gtk_widget_hide gtk_widget_hide = nullptr;
|
||||
|
@ -245,6 +242,10 @@ f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels = nullptr;
|
|||
f_gdk_pixbuf_get_width gdk_pixbuf_get_width = nullptr;
|
||||
f_gdk_pixbuf_get_height gdk_pixbuf_get_height = nullptr;
|
||||
f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride = nullptr;
|
||||
|
||||
bool GtkLoaded() {
|
||||
return gtkLoaded;
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
void start() {
|
||||
|
@ -255,7 +256,6 @@ void start() {
|
|||
|
||||
DEBUG_LOG(("Loading libraries"));
|
||||
|
||||
bool gtkLoaded = false;
|
||||
QLibrary lib_gtk;
|
||||
lib_gtk.setLoadHints(QLibrary::DeepBindHint);
|
||||
|
||||
|
@ -284,6 +284,11 @@ void start() {
|
|||
|
||||
const auto settings = gtk_settings_get_default();
|
||||
g_signal_connect(settings, "notify::gtk-icon-theme-name", G_CALLBACK(SetIconTheme), nullptr);
|
||||
g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(DarkModeChanged), nullptr);
|
||||
|
||||
if (!gtk_check_version(3, 0, 0)) {
|
||||
g_signal_connect(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(DarkModeChanged), nullptr);
|
||||
}
|
||||
} else {
|
||||
LOG(("Could not load gtk-3 or gtk-x11-2.0!"));
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ extern "C" {
|
|||
namespace Platform {
|
||||
namespace Libs {
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
bool GtkLoaded();
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
void start();
|
||||
|
||||
template <typename Function>
|
||||
|
@ -50,6 +54,9 @@ bool load(QLibrary &lib, const char *name, Function &func) {
|
|||
typedef gboolean (*f_gtk_init_check)(int *argc, char ***argv);
|
||||
extern f_gtk_init_check gtk_init_check;
|
||||
|
||||
typedef const gchar* (*f_gtk_check_version)(guint required_major, guint required_minor, guint required_micro);
|
||||
extern f_gtk_check_version gtk_check_version;
|
||||
|
||||
typedef GtkSettings* (*f_gtk_settings_get_default)(void);
|
||||
extern f_gtk_settings_get_default gtk_settings_get_default;
|
||||
|
||||
|
@ -252,6 +259,25 @@ extern f_gdk_pixbuf_get_height gdk_pixbuf_get_height;
|
|||
|
||||
typedef int (*f_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf);
|
||||
extern f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride;
|
||||
|
||||
inline bool GtkSettingSupported() {
|
||||
return gtk_settings_get_default != nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T GtkSetting(const gchar *propertyName) {
|
||||
GtkSettings *settings = gtk_settings_get_default();
|
||||
T value;
|
||||
g_object_get(settings, propertyName, &value, nullptr);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline QString GtkSetting(const gchar *propertyName) {
|
||||
gchararray value = GtkSetting<gchararray>(propertyName);
|
||||
QString str = QString::fromUtf8(value);
|
||||
g_free(value);
|
||||
return str;
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
} // namespace Libs
|
||||
|
|
|
@ -869,6 +869,27 @@ std::optional<crl::time> LastUserInputTime() {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<bool> IsDarkMode() {
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
if (Libs::GtkSettingSupported()
|
||||
&& Libs::GtkLoaded()) {
|
||||
if (Libs::gtk_check_version != nullptr
|
||||
&& !Libs::gtk_check_version(3, 0, 0)
|
||||
&& Libs::GtkSetting<gboolean>("gtk-application-prefer-dark-theme")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Libs::GtkSetting("gtk-theme-name").toLower().endsWith(qsl("-dark"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool AutostartSupported() {
|
||||
// snap sandbox doesn't allow creating files in folders with names started with a dot
|
||||
// and doesn't provide any api to add an app to autostart
|
||||
|
|
|
@ -18,6 +18,10 @@ namespace Platform {
|
|||
|
||||
void RemoveQuarantine(const QString &path);
|
||||
|
||||
inline std::optional<bool> IsDarkMode() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline void FallbackFontConfigCheckBegin() {
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,11 @@ bool OpenSystemSettings(SystemSettingsType type);
|
|||
return LastUserInputTime().has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<bool> IsDarkMode();
|
||||
[[nodiscard]] inline bool IsDarkModeSupported() {
|
||||
return IsDarkMode().has_value();
|
||||
}
|
||||
|
||||
void IgnoreApplicationActivationRightNow();
|
||||
bool AutostartSupported();
|
||||
QImage GetImageFromClipboard();
|
||||
|
|
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/localstorage.h"
|
||||
#include "core/crash_reports.h"
|
||||
|
||||
#include <QtCore/QOperatingSystemVersion>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
@ -382,6 +383,30 @@ std::optional<crl::time> LastUserInputTime() {
|
|||
return LastTrackedWhen;
|
||||
}
|
||||
|
||||
std::optional<bool> IsDarkMode() {
|
||||
if (QOperatingSystemVersion::current()
|
||||
< QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
LPCWSTR lpKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||
LPCWSTR lpValueName = L"AppsUseLightTheme";
|
||||
HKEY key;
|
||||
auto result = RegOpenKeyEx(HKEY_CURRENT_USER, lpKeyName, 0, KEY_READ, &key);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DWORD value = 0, type = 0, size = sizeof(value);
|
||||
result = RegQueryValueEx(key, lpValueName, 0, &type, (LPBYTE)&value, &size);
|
||||
RegCloseKey(key);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
bool AutostartSupported() {
|
||||
return !IsWindowsStoreBuild();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "platform/win/windows_event_filter.h"
|
||||
|
||||
#include "platform/win/windows_dlls.h"
|
||||
#include "platform/win/specific_win.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/application.h"
|
||||
#include "ui/inactive_press.h"
|
||||
#include "mainwindow.h"
|
||||
|
@ -274,6 +276,10 @@ bool EventFilter::mainWindowEvent(
|
|||
}
|
||||
} return true;
|
||||
|
||||
case WM_SETTINGCHANGE: {
|
||||
Core::App().settings().setSystemDarkMode(Platform::IsDarkMode());
|
||||
} return false;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -120,6 +120,14 @@ auto GenerateCodes() {
|
|||
window->showSettings(Settings::Type::Folders);
|
||||
}
|
||||
});
|
||||
codes.emplace(qsl("autodark"), [](SessionController *window) {
|
||||
auto text = Core::App().settings().systemDarkModeEnabled() ? qsl("Disable system dark mode?") : qsl("Enable system dark mode?");
|
||||
Ui::show(Box<ConfirmBox>(text, [=] {
|
||||
Core::App().settings().setSystemDarkModeEnabled(!Core::App().settings().systemDarkModeEnabled());
|
||||
Core::App().saveSettingsDelayed();
|
||||
Ui::hideLayer();
|
||||
}));
|
||||
});
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
|
||||
codes.emplace(qsl("registertg"), [](SessionController *window) {
|
||||
|
|
|
@ -902,6 +902,19 @@ void MainMenu::refreshMenu() {
|
|||
*_nightThemeAction = action;
|
||||
action->setCheckable(true);
|
||||
action->setChecked(Window::Theme::IsNightMode());
|
||||
Core::App().settings().systemDarkModeValue(
|
||||
) | rpl::start_with_next([=](std::optional<bool> darkMode) {
|
||||
const auto darkModeEnabled = Core::App().settings().systemDarkModeEnabled();
|
||||
if (darkModeEnabled && darkMode.has_value()) {
|
||||
action->setChecked(*darkMode);
|
||||
}
|
||||
action->setEnabled(!darkModeEnabled || !darkMode.has_value());
|
||||
}, lifetime());
|
||||
Core::App().settings().systemDarkModeEnabledChanges(
|
||||
) | rpl::start_with_next([=](bool darkModeEnabled) {
|
||||
const auto darkMode = Core::App().settings().systemDarkMode();
|
||||
action->setEnabled(!darkModeEnabled || !darkMode.has_value());
|
||||
}, lifetime());
|
||||
_menu->finishAnimating();
|
||||
|
||||
updatePhone();
|
||||
|
|
Loading…
Add table
Reference in a new issue