Skip to content

Commit

Permalink
Add custom light and dark UI themes
Browse files Browse the repository at this point in the history
  • Loading branch information
phoerious committed Mar 4, 2020
1 parent 8899a3d commit 03d9e5b
Show file tree
Hide file tree
Showing 39 changed files with 6,408 additions and 357 deletions.
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ set(keepassx_SOURCES
format/OpVaultReaderAttachments.cpp
format/OpVaultReaderBandEntry.cpp
format/OpVaultReaderSections.cpp
gui/styles/styles.qrc
gui/styles/base/phantomcolor.cpp
gui/styles/base/BaseStyle.cpp
gui/styles/dark/DarkStyle.cpp
gui/styles/light/LightStyle.cpp
gui/AboutDialog.cpp
gui/Application.cpp
gui/CategoryListWidget.cpp
Expand Down
1 change: 1 addition & 0 deletions src/core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ void Config::init(const QString& fileName)
m_defaults.insert("GUI/HidePasswords", true);
m_defaults.insert("GUI/AdvancedSettings", false);
m_defaults.insert("GUI/MonospaceNotes", false);
m_defaults.insert("GUI/ApplicationTheme", "auto");
}

Config* Config::instance()
Expand Down
78 changes: 44 additions & 34 deletions src/core/FilePath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@

#include "FilePath.h"

#include <QCoreApplication>
#include <QBitmap>
#include <QDir>
#include <QLibrary>
#include <QStyle>

#include "config-keepassx.h"
#include "core/Config.h"
#include "core/Global.h"
#include "gui/MainWindow.h"

FilePath* FilePath::m_instance(nullptr);

Expand Down Expand Up @@ -98,7 +100,7 @@ QIcon FilePath::applicationIcon()
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc", false);
#else
return icon("apps", "keepassxc");
return icon("apps", "keepassxc", false);
#endif
}

Expand All @@ -109,7 +111,7 @@ QIcon FilePath::trayIcon()
#ifdef KEEPASSXC_DIST_SNAP
return (darkIcon) ? icon("apps", "keepassxc-dark", false) : icon("apps", "keepassxc", false);
#else
return (darkIcon) ? icon("apps", "keepassxc-dark") : icon("apps", "keepassxc");
return (darkIcon) ? icon("apps", "keepassxc-dark", false) : icon("apps", "keepassxc", false);
#endif
}

Expand All @@ -118,7 +120,7 @@ QIcon FilePath::trayIconLocked()
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc-locked", false);
#else
return icon("apps", "keepassxc-locked");
return icon("apps", "keepassxc-locked", false);
#endif
}

Expand All @@ -129,14 +131,13 @@ QIcon FilePath::trayIconUnlocked()
#ifdef KEEPASSXC_DIST_SNAP
return darkIcon ? icon("apps", "keepassxc-dark", false) : icon("apps", "keepassxc-unlocked", false);
#else
return darkIcon ? icon("apps", "keepassxc-dark") : icon("apps", "keepassxc-unlocked");
return darkIcon ? icon("apps", "keepassxc-dark", false) : icon("apps", "keepassxc-unlocked", false);
#endif
}

QIcon FilePath::icon(const QString& category, const QString& name)
QIcon FilePath::icon(const QString& category, const QString& name, bool recolor)
{
QString combinedName = category + "/" + name;

QIcon icon = m_iconCache.value(combinedName);

if (!icon.isNull()) {
Expand All @@ -154,7 +155,30 @@ QIcon FilePath::icon(const QString& category, const QString& name)
}
}
filename = QString("%1/icons/application/scalable/%2.svg").arg(m_dataPath, combinedName);
if (QFile::exists(filename)) {
if (QFile::exists(filename) && getMainWindow() && recolor) {
QPalette palette = getMainWindow()->palette();

QFile f(filename);
QIcon scalable(filename);
QPixmap pixmap = scalable.pixmap({128, 128});

auto mask = QBitmap::fromImage(pixmap.toImage().createAlphaMask());
pixmap.fill(palette.color(QPalette::WindowText));
pixmap.setMask(mask);
icon.addPixmap(pixmap, QIcon::Mode::Normal);

pixmap.fill(palette.color(QPalette::HighlightedText));
pixmap.setMask(mask);
icon.addPixmap(pixmap, QIcon::Mode::Selected);

pixmap.fill(palette.color(QPalette::Disabled, QPalette::WindowText));
pixmap.setMask(mask);
icon.addPixmap(pixmap, QIcon::Mode::Disabled);

#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
icon.setIsMask(true);
#endif
} else if (QFile::exists(filename)) {
icon.addFile(filename);
}
}
Expand All @@ -164,7 +188,7 @@ QIcon FilePath::icon(const QString& category, const QString& name)
return icon;
}

QIcon FilePath::onOffIcon(const QString& category, const QString& name)
QIcon FilePath::onOffIcon(const QString& category, const QString& name, bool recolor)
{
QString combinedName = category + "/" + name;
QString cacheName = "onoff/" + combinedName;
Expand All @@ -175,31 +199,17 @@ QIcon FilePath::onOffIcon(const QString& category, const QString& name)
return icon;
}

for (int i = 0; i < 2; i++) {
QIcon::State state;
QString stateName;

if (i == 0) {
state = QIcon::Off;
stateName = "off";
} else {
state = QIcon::On;
stateName = "on";
}

const QList<int> pngSizes = {16, 22, 24, 32, 48, 64, 128};
QString filename;
for (int size : pngSizes) {
filename = QString("%1/icons/application/%2x%2/%3-%4.png")
.arg(m_dataPath, QString::number(size), combinedName, stateName);
if (QFile::exists(filename)) {
icon.addFile(filename, QSize(size, size), QIcon::Normal, state);
}
}
filename = QString("%1/icons/application/scalable/%2-%3.svg").arg(m_dataPath, combinedName, stateName);
if (QFile::exists(filename)) {
icon.addFile(filename, QSize(), QIcon::Normal, state);
}
QIcon on = FilePath::icon(category, name + "-on", recolor);
for (const auto& size : on.availableSizes()) {
icon.addPixmap(on.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::On);
icon.addPixmap(on.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::On);
icon.addPixmap(on.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::On);
}
QIcon off = FilePath::icon(category, name + "-off", recolor);
for (const auto& size : off.availableSizes()) {
icon.addPixmap(off.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::Off);
icon.addPixmap(off.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::Off);
icon.addPixmap(off.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::Off);
}

m_iconCache.insert(cacheName, icon);
Expand Down
4 changes: 2 additions & 2 deletions src/core/FilePath.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class FilePath
QIcon trayIcon();
QIcon trayIconLocked();
QIcon trayIconUnlocked();
QIcon icon(const QString& category, const QString& name);
QIcon onOffIcon(const QString& category, const QString& name);
QIcon icon(const QString& category, const QString& name, bool recolor = true);
QIcon onOffIcon(const QString& category, const QString& name, bool recolor = true);

static FilePath* instance();

Expand Down
19 changes: 13 additions & 6 deletions src/gui/ApplicationSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ void ApplicationSettingsWidget::loadSettings()
m_generalUi->toolbarMovableCheckBox->setChecked(config()->get("GUI/MovableToolbar").toBool());
m_generalUi->monospaceNotesCheckBox->setChecked(config()->get("GUI/MonospaceNotes").toBool());

m_generalUi->appThemeSelection->clear();
m_generalUi->appThemeSelection->addItem(tr("Automatic"), QStringLiteral("auto"));
m_generalUi->appThemeSelection->addItem(tr("Light"), QStringLiteral("light"));
m_generalUi->appThemeSelection->addItem(tr("Dark"), QStringLiteral("dark"));
m_generalUi->appThemeSelection->addItem(tr("Classic (Platform-native)"), QStringLiteral("classic"));
m_generalUi->appThemeSelection->setCurrentIndex(
m_generalUi->appThemeSelection->findData(config()->get("GUI/ApplicationTheme").toString()));

m_generalUi->toolButtonStyleComboBox->clear();
m_generalUi->toolButtonStyleComboBox->addItem(tr("Icon only"), Qt::ToolButtonIconOnly);
m_generalUi->toolButtonStyleComboBox->addItem(tr("Text only"), Qt::ToolButtonTextOnly);
Expand Down Expand Up @@ -303,19 +311,18 @@ void ApplicationSettingsWidget::saveSettings()
config()->set("IgnoreGroupExpansion", m_generalUi->ignoreGroupExpansionCheckBox->isChecked());
config()->set("AutoTypeEntryTitleMatch", m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked());
config()->set("AutoTypeEntryURLMatch", m_generalUi->autoTypeEntryURLMatchCheckBox->isChecked());
int currentLangIndex = m_generalUi->languageComboBox->currentIndex();
config()->set("FaviconDownloadTimeout", m_generalUi->faviconTimeoutSpinBox->value());

config()->set("GUI/Language", m_generalUi->languageComboBox->itemData(currentLangIndex).toString());

config()->set("GUI/Language", m_generalUi->languageComboBox->currentData().toString());
config()->set("GUI/HidePreviewPanel", m_generalUi->previewHideCheckBox->isChecked());
config()->set("GUI/HideToolbar", m_generalUi->toolbarHideCheckBox->isChecked());
config()->set("GUI/MovableToolbar", m_generalUi->toolbarMovableCheckBox->isChecked());
config()->set("GUI/MonospaceNotes", m_generalUi->monospaceNotesCheckBox->isChecked());

int currentToolButtonStyleIndex = m_generalUi->toolButtonStyleComboBox->currentIndex();
config()->set("GUI/ToolButtonStyle",
m_generalUi->toolButtonStyleComboBox->itemData(currentToolButtonStyleIndex).toString());
QString theme = m_generalUi->appThemeSelection->currentData().toString();
config()->set("GUI/ApplicationTheme", theme);

config()->set("GUI/ToolButtonStyle", m_generalUi->toolButtonStyleComboBox->currentData().toString());

config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked());
config()->set("GUI/DarkTrayIcon", m_generalUi->systrayDarkIconCheckBox->isChecked());
Expand Down
61 changes: 48 additions & 13 deletions src/gui/ApplicationSettingsWidgetGeneral.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>684</width>
<height>951</height>
<width>499</width>
<height>1174</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
Expand Down Expand Up @@ -337,7 +337,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
<item>
<widget class="QLabel" name="faviconTimeoutLabel">
<property name="text">
Expand All @@ -350,12 +350,6 @@
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
Expand Down Expand Up @@ -400,6 +394,50 @@
<string>General</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="appThemeLabel">
<property name="text">
<string>Application Theme:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="appThemeSelection">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="accessibleName">
<string>Application Theme Selection</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>(restart program to activate)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="toolbarHideCheckBox">
<property name="text">
Expand Down Expand Up @@ -491,9 +529,6 @@
</item>
<item>
<widget class="QComboBox" name="toolButtonStyleComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
Expand Down Expand Up @@ -726,7 +761,7 @@
<item>
<widget class="QPushButton" name="resetSettingsButton">
<property name="text">
<string>Reset Settings to Default</string>
<string>Reset settings to default…</string>
</property>
</widget>
</item>
Expand Down
17 changes: 5 additions & 12 deletions src/gui/CategoryListWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <QListWidget>
#include <QPainter>
#include <QProxyStyle>
#include <QScrollBar>
#include <QSize>
#include <QStyledItemDelegate>
Expand Down Expand Up @@ -158,9 +159,7 @@ CategoryListWidgetDelegate::CategoryListWidgetDelegate(QListWidget* parent)
}
}

#ifdef Q_OS_WIN
#include <QProxyStyle>
class WindowsCorrectedStyle : public QProxyStyle
class IconSelectionCorrectedStyle : public QProxyStyle
{
public:
void drawPrimitive(PrimitiveElement element,
Expand All @@ -171,8 +170,8 @@ class WindowsCorrectedStyle : public QProxyStyle
painter->save();

if (PE_PanelItemViewItem == element) {
// Qt on Windows draws selection backgrounds only for the actual text/icon
// bounding box, not over the full width of a list item.
// Qt on Windows and the Fusion/Phantom base styles draw selection backgrounds only for
// the actual text/icon bounding box, not over the full width of a list item.
// We therefore need to translate and stretch the painter before we can
// tell Qt to draw its native styles.
// Since we are scaling horizontally, we also need to move the right and left
Expand All @@ -186,7 +185,6 @@ class WindowsCorrectedStyle : public QProxyStyle
painter->restore();
}
};
#endif

void CategoryListWidgetDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
Expand All @@ -203,12 +201,7 @@ void CategoryListWidgetDelegate::paint(QPainter* painter,
opt.decorationAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
opt.decorationPosition = QStyleOptionViewItem::Top;

#ifdef Q_OS_WIN
QScopedPointer<QStyle> style(new WindowsCorrectedStyle());
#else
QStyle* style = opt.widget ? opt.widget->style() : QApplication::style();
#endif

QScopedPointer<QStyle> style(new IconSelectionCorrectedStyle());
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);

QRect fontRect = painter->fontMetrics().boundingRect(
Expand Down
6 changes: 0 additions & 6 deletions src/gui/DatabaseOpenWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
m_ui->yubikeyProgress->setVisible(false);
#endif

#ifdef Q_OS_MACOS
// add random padding to layouts to align widgets properly
m_ui->dialogButtonsLayout->setContentsMargins(10, 0, 15, 0);
m_ui->gridLayout->setContentsMargins(10, 0, 0, 0);
#endif

#ifndef WITH_XC_TOUCHID
m_ui->touchIDContainer->setVisible(false);
#else
Expand Down
Loading

0 comments on commit 03d9e5b

Please sign in to comment.