Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: Introduce system theme detection for macOS #340

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ QT_MOC_CPP = \
qml/models/moc_options_model.cpp \
qml/models/moc_peerlistsortproxy.cpp \
qml/moc_appmode.cpp \
qml/moc_thememanager.cpp \
qt/moc_addressbookpage.cpp \
qt/moc_addresstablemodel.cpp \
qt/moc_askpassphrasedialog.cpp \
Expand Down Expand Up @@ -124,6 +125,7 @@ BITCOIN_QT_H = \
qml/appmode.h \
qml/bitcoin.h \
qml/imageprovider.h \
qml/thememanager.h \
qml/util.h \
qt/addressbookpage.h \
qt/addresstablemodel.h \
Expand Down Expand Up @@ -309,6 +311,7 @@ BITCOIN_QML_BASE_CPP = \
qml/models/options_model.cpp \
qml/models/peerlistsortproxy.cpp \
qml/imageprovider.cpp \
qml/thememanager.cpp \
qml/util.cpp

QML_RES_FONTS = \
Expand Down
3 changes: 3 additions & 0 deletions src/qml/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <qml/models/options_model.h>
#include <qml/models/peerlistsortproxy.h>
#include <qml/imageprovider.h>
#include <qml/thememanager.h>
#include <qml/util.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
Expand Down Expand Up @@ -243,6 +244,7 @@ int QmlGuiMain(int argc, char* argv[])
QObject::connect(&init_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection);
// QObject::connect(&init_executor, &InitExecutor::runawayException, &node_model, &NodeModel::handleRunawayException);

ThemeManager theme_manager{};
NetworkTrafficTower network_traffic_tower{node_model};
#ifdef __ANDROID__
AndroidNotifier android_notifier{node_model};
Expand Down Expand Up @@ -278,6 +280,7 @@ int QmlGuiMain(int argc, char* argv[])
engine.rootContext()->setContextProperty("chainModel", &chain_model);
engine.rootContext()->setContextProperty("peerTableModel", &peer_model);
engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
engine.rootContext()->setContextProperty("themeManager", &theme_manager);

OptionsQmlModel options_model{*node};
engine.rootContext()->setContextProperty("optionsModel", &options_model);
Expand Down
33 changes: 29 additions & 4 deletions src/qml/components/ThemeSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ ColumnLayout {
header: qsTr("Light")
actionItem: Icon {
anchors.centerIn: parent
visible: !Theme.dark
visible: !Theme.manualDark && Theme.manualTheme
source: "image://images/check"
color: Theme.color.neutral9
size: 24
}
onClicked: {
Theme.dark = false
Theme.manualTheme = true;
Theme.manualDark = false;
}
}
Separator { Layout.fillWidth: true }
Expand All @@ -36,13 +37,37 @@ ColumnLayout {
header: qsTr("Dark")
actionItem: Icon {
anchors.centerIn: parent
visible: Theme.dark
visible: Theme.manualDark && Theme.manualTheme
source: "image://images/check"
color: Theme.color.neutral9
size: 24
}
onClicked: {
Theme.dark = true;
Theme.manualTheme = true;
Theme.manualDark = true;
}
}
Separator { Layout.fillWidth: true }
Setting {
id: systemThemeSetting
property bool systemThemeAvailable: Theme.systemThemeAvailable
Layout.fillWidth: true
header: qsTr("System")
actionItem: Icon {
anchors.centerIn: parent
visible: !Theme.manualTheme
source: "image://images/check"
size: 24
}
Component.onCompleted: {
if (systemThemeAvailable) {
systemThemeSetting.state = "FILLED"
} else {
systemThemeSetting.state = "DISABLED"
}
}
onClicked: {
Theme.manualTheme = false
}
}
}
21 changes: 20 additions & 1 deletion src/qml/controls/Theme.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,38 @@ import QtQuick 2.15
import QtQuick.Controls 2.15
import Qt.labs.settings 1.0

import org.bitcoincore.qt 1.0

Control {
id: root
property bool dark: true
property bool dark: manualTheme ? manualDark : themeManager.darkMode
property bool systemThemeAvailable: themeManager.systemThemeAvailable
property bool manualTheme: false
property bool manualDark: true
property real blockclocksize: (5/12)
readonly property ColorSet color: dark ? darkColorSet : lightColorSet
readonly property ImageSet image: dark ? darkImageSet : lightImageSet

Settings {
id: settings
property alias dark: root.dark
property alias manualTheme: root.manualTheme
property alias manualDark: root.manualDark
property alias blockclocksize: root.blockclocksize
}

SystemPalette {
id: systemColor

onBaseChanged: {
themeManager.systemBaseColor = systemColor.base
}
}

Component.onCompleted: {
themeManager.systemBaseColor = systemColor.base
}

component ColorSet: QtObject {
required property color white
required property color background
Expand Down
48 changes: 48 additions & 0 deletions src/qml/thememanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qml/thememanager.h>

ThemeManager::ThemeManager(QObject* parent)
: QObject(parent)
{
}

void ThemeManager::setSystemBaseColor(QColor base_color)
{
// Convert QColor's 8-bit RGB values to linear RGB values
double linearR = base_color.redF();
double linearG = base_color.greenF();
double linearB = base_color.blueF();

// Constants for the luminance formula
const double RED_FACTOR = 0.2126;
const double GREEN_FACTOR = 0.7152;
const double BLUE_FACTOR = 0.0722;

// Calculate luminance using the formula
double luminance = RED_FACTOR * linearR + GREEN_FACTOR * linearG + BLUE_FACTOR * linearB;

if (luminance <= 0.5) {
m_dark_mode = true;
} else {
m_dark_mode = false;
}

m_system_base_color = base_color;

#ifdef Q_OS_MAC
setSystemThemeAvailable(true);
#endif

Q_EMIT darkModeChanged();
}

void ThemeManager::setSystemThemeAvailable(bool available)
{
if (m_system_theme_available != available) {
m_system_theme_available = available;
Q_EMIT systemThemeAvailableChanged();
}
}
41 changes: 41 additions & 0 deletions src/qml/thememanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_QML_THEMEMANAGER_H
#define BITCOIN_QML_THEMEMANAGER_H

#include <QObject>
#include <QColor>
#include <QProcess>


class ThemeManager : public QObject
{
Q_OBJECT
Q_PROPERTY(bool darkMode READ darkMode NOTIFY darkModeChanged)
Q_PROPERTY(QColor systemBaseColor READ systemBaseColor WRITE setSystemBaseColor)
Q_PROPERTY(bool systemThemeAvailable READ systemThemeAvailable WRITE setSystemThemeAvailable NOTIFY systemThemeAvailableChanged)

public:
explicit ThemeManager(QObject* parent = nullptr);

bool darkMode() const { return m_dark_mode; };
QColor systemBaseColor() const { return m_system_base_color; };
bool systemThemeAvailable() const { return m_system_theme_available; };

public Q_SLOTS:
void setSystemBaseColor(QColor base_color);
void setSystemThemeAvailable(bool available);

Q_SIGNALS:
void darkModeChanged();
void systemThemeAvailableChanged();

private:
bool m_dark_mode{ true };
QColor m_system_base_color;
bool m_system_theme_available{ false };
};

#endif // BITCOIN_QML_THEMEMANAGER_H