mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Add custom light and dark UI themes
This commit is contained in:
parent
6d2ca74878
commit
557736ea5e
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()) {
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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);
|
||||
|
@ -32,8 +32,8 @@ public:
|
||||
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();
|
||||
|
||||
|
@ -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);
|
||||
@ -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());
|
||||
|
@ -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">
|
||||
@ -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">
|
||||
@ -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>
|
||||
@ -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">
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <QListWidget>
|
||||
#include <QPainter>
|
||||
#include <QProxyStyle>
|
||||
#include <QScrollBar>
|
||||
#include <QSize>
|
||||
#include <QStyledItemDelegate>
|
||||
@ -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,
|
||||
@ -171,8 +170,8 @@ public:
|
||||
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
|
||||
@ -186,7 +185,6 @@ public:
|
||||
painter->restore();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
void CategoryListWidgetDelegate::paint(QPainter* painter,
|
||||
const QStyleOptionViewItem& option,
|
||||
@ -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(
|
||||
|
@ -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
|
||||
|
@ -99,7 +99,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="horizontalFrame">
|
||||
<widget class="QFrame" name="loginFrame">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>550</width>
|
||||
@ -235,142 +235,6 @@
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="1" column="3">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="2">
|
||||
<widget class="QProgressBar" name="yubikeyProgress">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>2</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="comboChallengeResponse">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Hardware key slot selection</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="buttonBrowseFile">
|
||||
<property name="toolTip">
|
||||
<string>Browse for key file</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Browse for key file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboKeyFile">
|
||||
<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="accessibleName">
|
||||
<string>Key file selection</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="hardwareKeyLabel">
|
||||
<property name="text">
|
||||
<string>Hardware Key:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comboChallengeResponse</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="hardwareKeyLabelHelp">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>You can use a hardware security key such as a <strong>YubiKey</strong> or <strong>OnlyKey</strong> with slots configured for HMAC-SHA1.</p>
|
||||
<p>Click for more information...</p></string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Hardware key help</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolButton {
|
||||
border: none;
|
||||
background: none;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">?</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>12</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="spacing">
|
||||
@ -419,22 +283,204 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="buttonRedetectYubikey">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<item row="1" column="3">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="2">
|
||||
<widget class="QProgressBar" name="yubikeyProgress">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>2</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="comboChallengeResponse">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Hardware key slot selection</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="hardwareKeyLabel">
|
||||
<property name="text">
|
||||
<string>Hardware Key:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comboChallengeResponse</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="hardwareKeyLabelHelp">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>You can use a hardware security key such as a <strong>YubiKey</strong> or <strong>OnlyKey</strong> with slots configured for HMAC-SHA1.</p>
|
||||
<p>Click for more information...</p></string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Hardware key help</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolButton {
|
||||
border: none;
|
||||
background: none;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">?</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>12</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>2</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboKeyFile">
|
||||
<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="accessibleName">
|
||||
<string>Key file selection</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="buttonBrowseFile">
|
||||
<property name="toolTip">
|
||||
<string>Refresh hardware tokens</string>
|
||||
<string>Browse for key file</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Refresh hardware tokens</string>
|
||||
<string>Browse for key file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRedetectYubikey">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Refresh hardware tokens</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Refresh hardware tokens</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>2</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -572,7 +618,6 @@
|
||||
<tabstop>buttonBrowseFile</tabstop>
|
||||
<tabstop>hardwareKeyLabelHelp</tabstop>
|
||||
<tabstop>comboChallengeResponse</tabstop>
|
||||
<tabstop>buttonRedetectYubikey</tabstop>
|
||||
<tabstop>checkTouchID</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
@ -80,8 +80,10 @@ void DatabaseTabWidget::toggleTabbar()
|
||||
{
|
||||
if (count() > 1) {
|
||||
tabBar()->show();
|
||||
emit tabVisibilityChanged(true);
|
||||
} else {
|
||||
tabBar()->hide();
|
||||
emit tabVisibilityChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ signals:
|
||||
void databaseLocked(DatabaseWidget* dbWidget);
|
||||
void activateDatabaseChanged(DatabaseWidget* dbWidget);
|
||||
void tabNameChanged();
|
||||
void tabVisibilityChanged(bool tabsVisible);
|
||||
void messageGlobal(const QString&, MessageWidget::MessageType type);
|
||||
void messageDismissGlobal();
|
||||
void databaseUnlockDialogFinished(bool accepted, DatabaseWidget* dbWidget);
|
||||
|
@ -101,9 +101,7 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
|
||||
*
|
||||
* NOTE:
|
||||
* If m_listViewState is empty, the list view has been activated for the first
|
||||
* time after starting with a clean (or invalid) config. Thus, save the current
|
||||
* state. Without this, m_listViewState would remain empty until there is an
|
||||
* actual view state change (e.g. column is resized)
|
||||
* time after starting with a clean (or invalid) config.
|
||||
*/
|
||||
void DatabaseWidgetStateSync::restoreListView()
|
||||
{
|
||||
@ -112,8 +110,6 @@ void DatabaseWidgetStateSync::restoreListView()
|
||||
|
||||
if (!m_listViewState.isEmpty()) {
|
||||
m_activeDbWidget->setEntryViewState(m_listViewState);
|
||||
} else {
|
||||
m_listViewState = m_activeDbWidget->entryViewState();
|
||||
}
|
||||
|
||||
m_blockUpdates = false;
|
||||
|
@ -24,6 +24,7 @@
|
||||
DialogyWidget::DialogyWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setAutoFillBackground(true);
|
||||
}
|
||||
|
||||
void DialogyWidget::keyPressEvent(QKeyEvent* e)
|
||||
|
@ -102,12 +102,6 @@ void KMessageWidgetPrivate::init(KMessageWidget *q_ptr)
|
||||
closeButton->setAutoRaise(true);
|
||||
closeButton->setDefaultAction(closeAction);
|
||||
closeButtonPixmap = QPixmap(closeButton->icon().pixmap(closeButton->icon().actualSize(QSize(16, 16))));
|
||||
#ifdef Q_OS_MACOS
|
||||
closeButton->setStyleSheet("QToolButton { background: transparent;"
|
||||
"border-radius: 2px; padding: 3px; }"
|
||||
"QToolButton::hover, QToolButton::focus {"
|
||||
"border: 1px solid rgb(90, 200, 250); }");
|
||||
#endif
|
||||
|
||||
q->setMessageType(KMessageWidget::Information);
|
||||
}
|
||||
@ -263,7 +257,7 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
||||
{
|
||||
d->messageType = type;
|
||||
QColor bg0, bg1, bg2, border;
|
||||
QColor fg = palette().light().color();
|
||||
QColor fg = QColor(238, 238, 238);
|
||||
switch (type) {
|
||||
case Positive:
|
||||
bg1.setRgb(37, 163, 83);
|
||||
@ -273,7 +267,7 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
||||
break;
|
||||
case Warning:
|
||||
bg1.setRgb(252, 193, 57);
|
||||
fg = palette().windowText().color();
|
||||
fg = QColor(48, 48, 48);
|
||||
break;
|
||||
case Error:
|
||||
bg1.setRgb(198, 69, 21);
|
||||
@ -294,9 +288,15 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
||||
painter.fillRect(QRect(0, 0, 16, 16), fg);
|
||||
painter.end();
|
||||
d->closeButton->setIcon(closeButtonPixmap);
|
||||
d->closeButton->setStyleSheet(QStringLiteral("QToolButton {"
|
||||
" background: transparent;"
|
||||
" border-radius: 2px;"
|
||||
" border: none; }"
|
||||
"QToolButton:hover, QToolButton:focus {"
|
||||
" border: 1px solid %1; }").arg(fg.name()));
|
||||
|
||||
d->content->setStyleSheet(
|
||||
QString(QLatin1String(".QFrame {"
|
||||
QStringLiteral(".QFrame {"
|
||||
"background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
|
||||
" stop: 0 %1,"
|
||||
" stop: 0.1 %2,"
|
||||
@ -307,7 +307,7 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
||||
" padding: 5px;"
|
||||
"}"
|
||||
".QLabel { color: %6; }"
|
||||
))
|
||||
)
|
||||
.arg(bg0.name(),
|
||||
bg1.name(),
|
||||
bg2.name(),
|
||||
|
@ -236,6 +236,9 @@ MainWindow::MainWindow()
|
||||
autoType()->registerGlobalShortcut(globalAutoTypeKey, globalAutoTypeModifiers);
|
||||
}
|
||||
|
||||
m_ui->toolbarSeparator->setVisible(false);
|
||||
m_showToolbarSeparator = config()->get("GUI/ApplicationTheme").toString() != "classic";
|
||||
|
||||
m_ui->actionEntryAutoType->setVisible(autoType()->isAvailable());
|
||||
|
||||
m_inactivityTimer = new InactivityTimer(this);
|
||||
@ -389,6 +392,7 @@ MainWindow::MainWindow()
|
||||
connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), m_searchWidget, SLOT(databaseChanged()));
|
||||
|
||||
connect(m_ui->tabWidget, SIGNAL(tabNameChanged()), SLOT(updateWindowTitle()));
|
||||
connect(m_ui->tabWidget, SIGNAL(tabVisibilityChanged(bool)), SLOT(adjustToTabVisibilityChange(bool)));
|
||||
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
|
||||
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(databaseTabChanged(int)));
|
||||
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
|
||||
@ -618,14 +622,18 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
bool inDatabaseTabWidgetOrWelcomeWidget = inDatabaseTabWidget || inWelcomeWidget;
|
||||
|
||||
m_ui->actionDatabaseMerge->setEnabled(inDatabaseTabWidget);
|
||||
|
||||
m_ui->actionDatabaseNew->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
||||
m_ui->actionDatabaseOpen->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
||||
m_ui->menuRecentDatabases->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
||||
m_ui->menuImport->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
||||
|
||||
m_ui->actionLockDatabases->setEnabled(m_ui->tabWidget->hasLockableDatabases());
|
||||
|
||||
if (m_showToolbarSeparator) {
|
||||
m_ui->toolbarSeparator->setVisible(
|
||||
(!inWelcomeWidget && inDatabaseTabWidget && !m_ui->tabWidget->tabBar()->isVisible())
|
||||
|| currentIndex == SettingsScreen);
|
||||
}
|
||||
|
||||
if (inDatabaseTabWidget && m_ui->tabWidget->currentIndex() != -1) {
|
||||
DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
||||
Q_ASSERT(dbWidget);
|
||||
@ -776,6 +784,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::adjustToTabVisibilityChange(bool tabsVisible)
|
||||
{
|
||||
if (m_showToolbarSeparator) {
|
||||
m_ui->toolbarSeparator->setVisible(!tabsVisible && m_ui->stackedWidget->currentIndex() == DatabaseTabScreen);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateWindowTitle()
|
||||
{
|
||||
QString customWindowTitlePart;
|
||||
@ -1125,7 +1140,7 @@ void MainWindow::updateTrayIcon()
|
||||
|
||||
QAction* actionToggle = new QAction(tr("Toggle window"), menu);
|
||||
menu->addAction(actionToggle);
|
||||
actionToggle->setIcon(filePath()->icon("apps", "keepassxc-dark"));
|
||||
actionToggle->setIcon(filePath()->icon("apps", "keepassxc-dark", false));
|
||||
|
||||
menu->addAction(m_ui->actionLockDatabases);
|
||||
|
||||
|
@ -86,6 +86,7 @@ protected:
|
||||
|
||||
private slots:
|
||||
void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::Mode::None);
|
||||
void adjustToTabVisibilityChange(bool tabsVisible);
|
||||
void updateWindowTitle();
|
||||
void showAboutDialog();
|
||||
void showUpdateCheckStartup();
|
||||
@ -167,6 +168,7 @@ private:
|
||||
bool m_appExitCalled = false;
|
||||
bool m_appExiting = false;
|
||||
bool m_contextMenuFocusLock = false;
|
||||
bool m_showToolbarSeparator = false;
|
||||
qint64 m_lastFocusOutTime = 0;
|
||||
qint64 m_lastShowTime = 0;
|
||||
QTimer m_trayIconTriggerTimer;
|
||||
|
@ -61,6 +61,16 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="toolbarSeparator">
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="sizePolicy">
|
||||
@ -195,7 +205,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>21</height>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
@ -210,7 +220,7 @@
|
||||
</property>
|
||||
<widget class="QMenu" name="menuRecentDatabases">
|
||||
<property name="title">
|
||||
<string>&Recent databases</string>
|
||||
<string>&Recent Databases</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuImport">
|
||||
@ -249,7 +259,6 @@
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionGettingStarted"/>
|
||||
<addaction name="actionUserGuide"/>
|
||||
@ -259,10 +268,11 @@
|
||||
<addaction name="actionCheckForUpdates"/>
|
||||
<addaction name="actionDonate"/>
|
||||
<addaction name="actionBugReport"/>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEntries">
|
||||
<property name="title">
|
||||
<string>E&ntries</string>
|
||||
<string>&Entries</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuEntryCopyAttribute">
|
||||
<property name="enabled">
|
||||
@ -272,7 +282,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Copy att&ribute...</string>
|
||||
<string>Copy Att&ribute</string>
|
||||
</property>
|
||||
<addaction name="actionEntryCopyTitle"/>
|
||||
<addaction name="actionEntryCopyURL"/>
|
||||
@ -284,7 +294,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>TOTP...</string>
|
||||
<string>TOTP</string>
|
||||
</property>
|
||||
<addaction name="actionEntryCopyTotp"/>
|
||||
<addaction name="actionEntryTotp"/>
|
||||
@ -311,7 +321,6 @@
|
||||
<string>&Groups</string>
|
||||
</property>
|
||||
<addaction name="actionGroupNew"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionGroupEdit"/>
|
||||
<addaction name="actionGroupDelete"/>
|
||||
<addaction name="actionGroupEmptyRecycleBin"/>
|
||||
@ -347,8 +356,8 @@
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
<width>26</width>
|
||||
<height>26</height>
|
||||
</size>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
@ -393,7 +402,7 @@
|
||||
</action>
|
||||
<action name="actionCheckForUpdates">
|
||||
<property name="text">
|
||||
<string>&Check for Updates...</string>
|
||||
<string>&Check for Updates</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::ApplicationSpecificRole</enum>
|
||||
@ -401,7 +410,7 @@
|
||||
</action>
|
||||
<action name="actionDatabaseOpen">
|
||||
<property name="text">
|
||||
<string>&Open database...</string>
|
||||
<string>&Open Database…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSave">
|
||||
@ -409,7 +418,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Save database</string>
|
||||
<string>&Save Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseClose">
|
||||
@ -417,12 +426,12 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close database</string>
|
||||
<string>&Close Database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseNew">
|
||||
<property name="text">
|
||||
<string>&New database...</string>
|
||||
<string>&New Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create a new database</string>
|
||||
@ -430,7 +439,7 @@
|
||||
</action>
|
||||
<action name="actionDatabaseMerge">
|
||||
<property name="text">
|
||||
<string>&Merge from database...</string>
|
||||
<string>&Merge From Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Merge from another KDBX database</string>
|
||||
@ -441,7 +450,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&New entry</string>
|
||||
<string>&New Entry…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add a new entry</string>
|
||||
@ -452,7 +461,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Edit entry</string>
|
||||
<string>&Edit Entry…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View or edit entry</string>
|
||||
@ -463,7 +472,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Delete entry</string>
|
||||
<string>&Delete Entry…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupNew">
|
||||
@ -471,7 +480,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&New group</string>
|
||||
<string>&New Group…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add a new group</string>
|
||||
@ -482,7 +491,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Edit group</string>
|
||||
<string>&Edit Group…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupDelete">
|
||||
@ -490,7 +499,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Delete group</string>
|
||||
<string>&Delete Group…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupDownloadFavicons">
|
||||
@ -498,7 +507,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Downlo&ad all favicons</string>
|
||||
<string>Download All &Favicons…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupSortAsc">
|
||||
@ -522,7 +531,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sa&ve database as...</string>
|
||||
<string>Sa&ve Database As…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChangeMasterKey">
|
||||
@ -530,7 +539,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change master &key...</string>
|
||||
<string>Change Master &Key…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReports">
|
||||
@ -552,7 +561,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Database settings...</string>
|
||||
<string>&Database Settings…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Database settings</string>
|
||||
@ -566,7 +575,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Clone entry</string>
|
||||
<string>&Clone Entry…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyUsername">
|
||||
@ -574,7 +583,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy &username</string>
|
||||
<string>Copy &Username</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy username to clipboard</string>
|
||||
@ -585,7 +594,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy &password</string>
|
||||
<string>Copy &Password</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy password to clipboard</string>
|
||||
@ -620,7 +629,7 @@
|
||||
</action>
|
||||
<action name="actionEntryDownloadIcon">
|
||||
<property name="text">
|
||||
<string>Download favicon</string>
|
||||
<string>Download &Favicon</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryOpenUrl">
|
||||
@ -636,7 +645,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Lock databases</string>
|
||||
<string>&Lock Databases</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyTitle">
|
||||
@ -677,7 +686,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Export to CSV file...</string>
|
||||
<string>&Export to CSV File…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportHtml">
|
||||
@ -685,12 +694,12 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Export to HTML file...</string>
|
||||
<string>&Export to HTML File…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImportKeePass1">
|
||||
<property name="text">
|
||||
<string>KeePass 1 database...</string>
|
||||
<string>KeePass 1 Database…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import a KeePass 1 database</string>
|
||||
@ -698,7 +707,7 @@
|
||||
</action>
|
||||
<action name="actionImportOpVault">
|
||||
<property name="text">
|
||||
<string>1Password Vault...</string>
|
||||
<string>1Password Vault…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import a 1Password Vault</string>
|
||||
@ -706,7 +715,7 @@
|
||||
</action>
|
||||
<action name="actionImportCsv">
|
||||
<property name="text">
|
||||
<string>CSV file...</string>
|
||||
<string>CSV File…</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Import a CSV file</string>
|
||||
@ -714,17 +723,17 @@
|
||||
</action>
|
||||
<action name="actionEntryTotp">
|
||||
<property name="text">
|
||||
<string>Show TOTP...</string>
|
||||
<string>Show TOTP</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryTotpQRCode">
|
||||
<property name="text">
|
||||
<string>Show TOTP QR Code...</string>
|
||||
<string>Show QR Code</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntrySetupTotp">
|
||||
<property name="text">
|
||||
<string>Set up TOTP...</string>
|
||||
<string>Set up TOTP…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyTotp">
|
||||
@ -747,7 +756,7 @@
|
||||
</action>
|
||||
<action name="actionBugReport">
|
||||
<property name="text">
|
||||
<string>Report a &bug</string>
|
||||
<string>Report a &Bug</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGettingStarted">
|
||||
@ -760,7 +769,7 @@
|
||||
</action>
|
||||
<action name="actionOnlineHelp">
|
||||
<property name="text">
|
||||
<string>&Online Help...</string>
|
||||
<string>&Online Help</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Go to online documentation (opens browser)</string>
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "core/PasswordGenerator.h"
|
||||
#include "core/PasswordHealth.h"
|
||||
#include "gui/Clipboard.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
@ -390,27 +391,33 @@ void PasswordGeneratorWidget::colorStrengthIndicator(const PasswordHealth& healt
|
||||
style.replace(re, "\\1 %1;");
|
||||
|
||||
// Set the color and background based on entropy
|
||||
// colors are taking from the KDE breeze palette
|
||||
// <https://community.kde.org/KDE_Visual_Design_Group/HIG/Color>
|
||||
QList<QString> qualityColors;
|
||||
if (osUtils->isDarkMode()) {
|
||||
qualityColors << QStringLiteral("#C43F31") << QStringLiteral("#DB9837") << QStringLiteral("#608A22")
|
||||
<< QStringLiteral("#1F8023");
|
||||
} else {
|
||||
qualityColors << QStringLiteral("#C43F31") << QStringLiteral("#E09932") << QStringLiteral("#5EA10E")
|
||||
<< QStringLiteral("#118f17");
|
||||
}
|
||||
switch (health.quality()) {
|
||||
case PasswordHealth::Quality::Bad:
|
||||
case PasswordHealth::Quality::Poor:
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg("#c0392b"));
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg(qualityColors[0]));
|
||||
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Poor", "Password quality")));
|
||||
break;
|
||||
|
||||
case PasswordHealth::Quality::Weak:
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg("#f39c1f"));
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg(qualityColors[1]));
|
||||
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Weak", "Password quality")));
|
||||
break;
|
||||
|
||||
case PasswordHealth::Quality::Good:
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg("#11d116"));
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg(qualityColors[2]));
|
||||
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Good", "Password quality")));
|
||||
break;
|
||||
|
||||
case PasswordHealth::Quality::Excellent:
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg("#27ae60"));
|
||||
m_ui->entropyProgressBar->setStyleSheet(style.arg(qualityColors[3]));
|
||||
m_ui->strengthLabel->setText(tr("Password Quality: %1").arg(tr("Excellent", "Password quality")));
|
||||
break;
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>334</width>
|
||||
<height>249</height>
|
||||
<width>487</width>
|
||||
<height>326</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -17,10 +17,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
@ -58,6 +55,9 @@
|
||||
<property name="text">
|
||||
<string>Search terms are as follows: [modifiers][field:]["]term["]</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -77,6 +77,9 @@
|
||||
<property name="text">
|
||||
<string>Every search term must match (ie, logical AND)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,4">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="4">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
@ -29,22 +29,6 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="searchEdit">
|
||||
<property name="sizePolicy">
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
@ -27,6 +26,7 @@
|
||||
#include "core/Config.h"
|
||||
#include "core/DatabaseIcons.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/Global.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
@ -218,9 +218,6 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case Totp:
|
||||
result = entry->hasTotp() ? tr("Yes") : "";
|
||||
return result;
|
||||
}
|
||||
} else if (role == Qt::UserRole) { // Qt::UserRole is used as sort role, see EntryView::EntryView()
|
||||
switch (index.column()) {
|
||||
@ -240,7 +237,9 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
|
||||
case Paperclip:
|
||||
// Display entries with attachments above those without when
|
||||
// sorting ascendingly (and vice versa when sorting descendingly)
|
||||
return entry->attachments()->isEmpty() ? 1 : 0;
|
||||
return !entry->attachments()->isEmpty();
|
||||
case Totp:
|
||||
return entry->hasTotp();
|
||||
default:
|
||||
// For all other columns, simply use data provided by Qt::Display-
|
||||
// Role for sorting
|
||||
@ -260,7 +259,12 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
|
||||
return entry->iconScaledPixmap();
|
||||
case Paperclip:
|
||||
if (!entry->attachments()->isEmpty()) {
|
||||
return m_paperClipPixmap;
|
||||
return filePath()->icon("actions", "paperclip");
|
||||
}
|
||||
break;
|
||||
case Totp:
|
||||
if (entry->hasTotp()) {
|
||||
return filePath()->icon("actions", "chronometer");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -327,16 +331,47 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro
|
||||
return tr("Accessed");
|
||||
case Attachments:
|
||||
return tr("Attachments");
|
||||
case Totp:
|
||||
return tr("TOTP");
|
||||
}
|
||||
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
if (section == Paperclip) {
|
||||
return m_paperClipPixmap;
|
||||
switch (section) {
|
||||
case Paperclip:
|
||||
return filePath()->icon("actions", "paperclip");
|
||||
case Totp:
|
||||
return filePath()->icon("actions", "chronometer");
|
||||
}
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
switch (section) {
|
||||
case ParentGroup:
|
||||
return tr("Group name");
|
||||
case Title:
|
||||
return tr("Entry title");
|
||||
case Username:
|
||||
return tr("Username");
|
||||
case Password:
|
||||
return tr("Password");
|
||||
case Url:
|
||||
return tr("URL");
|
||||
case Notes:
|
||||
return tr("Entry notes");
|
||||
case Expires:
|
||||
return tr("Entry expires at");
|
||||
case Created:
|
||||
return tr("Creation date");
|
||||
case Modified:
|
||||
return tr("Last modification date");
|
||||
case Accessed:
|
||||
return tr("Last access date");
|
||||
case Attachments:
|
||||
return tr("Attached files");
|
||||
case Paperclip:
|
||||
return tr("Has attachments");
|
||||
case Totp:
|
||||
return tr("Has TOTP one-time password");
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
return {};
|
||||
}
|
||||
|
||||
Qt::DropActions EntryModel::supportedDropActions() const
|
||||
@ -502,8 +537,3 @@ void EntryModel::setPasswordsHidden(bool hide)
|
||||
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
|
||||
emit passwordsHiddenChanged();
|
||||
}
|
||||
|
||||
void EntryModel::setPaperClipPixmap(const QPixmap& paperclip)
|
||||
{
|
||||
m_paperClipPixmap = paperclip;
|
||||
}
|
||||
|
@ -68,8 +68,6 @@ public:
|
||||
bool isPasswordsHidden() const;
|
||||
void setPasswordsHidden(bool hide);
|
||||
|
||||
void setPaperClipPixmap(const QPixmap& paperclip);
|
||||
|
||||
signals:
|
||||
void usernamesHiddenChanged();
|
||||
void passwordsHiddenChanged();
|
||||
@ -93,8 +91,6 @@ private:
|
||||
bool m_hideUsernames;
|
||||
bool m_hidePasswords;
|
||||
|
||||
QPixmap m_paperClipPixmap;
|
||||
|
||||
const QString HiddenContentDisplay;
|
||||
const Qt::DateFormat DateFormat;
|
||||
};
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <QMenu>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "core/FilePath.h"
|
||||
#include "gui/SortFilterHideProxyModel.h"
|
||||
|
||||
EntryView::EntryView(QWidget* parent)
|
||||
@ -70,22 +69,31 @@ EntryView::EntryView(QWidget* parent)
|
||||
m_hidePasswordsAction->setCheckable(true);
|
||||
m_headerMenu->addSeparator();
|
||||
|
||||
resetViewToDefaults();
|
||||
|
||||
// Actions to toggle column visibility, each carrying the corresponding
|
||||
// colummn index as data
|
||||
m_columnActions = new QActionGroup(this);
|
||||
m_columnActions->setExclusive(false);
|
||||
for (int columnIndex = 1; columnIndex < header()->count(); ++columnIndex) {
|
||||
QString caption = m_model->headerData(columnIndex, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||
if (columnIndex == EntryModel::Paperclip) {
|
||||
caption = tr("Attachments (icon)");
|
||||
for (int visualIndex = 1; visualIndex < header()->count(); ++visualIndex) {
|
||||
int logicalIndex = header()->logicalIndex(visualIndex);
|
||||
QString caption = m_model->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||
if (logicalIndex == EntryModel::Paperclip) {
|
||||
caption = tr("Has attachments", "Entry attachment icon toggle");
|
||||
} else if (logicalIndex == EntryModel::Totp) {
|
||||
caption = tr("Has TOTP", "Entry TOTP icon toggle");
|
||||
}
|
||||
|
||||
QAction* action = m_headerMenu->addAction(caption);
|
||||
action->setCheckable(true);
|
||||
action->setData(columnIndex);
|
||||
action->setData(logicalIndex);
|
||||
m_columnActions->addAction(action);
|
||||
}
|
||||
connect(m_columnActions, SIGNAL(triggered(QAction*)), this, SLOT(toggleColumnVisibility(QAction*)));
|
||||
connect(header(), &QHeaderView::sortIndicatorChanged, [this](int index, Qt::SortOrder order) {
|
||||
Q_UNUSED(order)
|
||||
header()->setSortIndicatorShown(index != EntryModel::Paperclip && index != EntryModel::Totp);
|
||||
});
|
||||
|
||||
m_headerMenu->addSeparator();
|
||||
m_headerMenu->addAction(tr("Fit to window"), this, SLOT(fitColumnsToWindow()));
|
||||
@ -114,22 +122,6 @@ EntryView::EntryView(QWidget* parent)
|
||||
// clang-format off
|
||||
connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SIGNAL(viewStateChanged()));
|
||||
// clang-format on
|
||||
|
||||
resetFixedColumns();
|
||||
|
||||
// Configure default search view state and save for later use
|
||||
header()->showSection(EntryModel::ParentGroup);
|
||||
m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder);
|
||||
sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder);
|
||||
m_defaultSearchViewState = header()->saveState();
|
||||
|
||||
// Configure default list view state and save for later use
|
||||
header()->hideSection(EntryModel::ParentGroup);
|
||||
m_sortModel->sort(EntryModel::Title, Qt::AscendingOrder);
|
||||
sortByColumn(EntryModel::Title, Qt::AscendingOrder);
|
||||
m_defaultListViewState = header()->saveState();
|
||||
|
||||
m_model->setPaperClipPixmap(filePath()->icon("actions", "paperclip").pixmap(16));
|
||||
}
|
||||
|
||||
void EntryView::contextMenuShortcutPressed()
|
||||
@ -325,6 +317,7 @@ bool EntryView::setViewState(const QByteArray& state)
|
||||
{
|
||||
bool status = header()->restoreState(state);
|
||||
resetFixedColumns();
|
||||
m_columnsNeedRelayout = state.isEmpty();
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -397,9 +390,11 @@ void EntryView::toggleColumnVisibility(QAction* action)
|
||||
*/
|
||||
void EntryView::fitColumnsToWindow()
|
||||
{
|
||||
header()->resizeSections(QHeaderView::Stretch);
|
||||
header()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
resetFixedColumns();
|
||||
QCoreApplication::processEvents();
|
||||
header()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
resetFixedColumns();
|
||||
fillRemainingWidth(true);
|
||||
emit viewStateChanged();
|
||||
}
|
||||
|
||||
@ -409,69 +404,88 @@ void EntryView::fitColumnsToWindow()
|
||||
*/
|
||||
void EntryView::fitColumnsToContents()
|
||||
{
|
||||
// Resize columns to fit contents
|
||||
header()->resizeSections(QHeaderView::ResizeToContents);
|
||||
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
resetFixedColumns();
|
||||
QCoreApplication::processEvents();
|
||||
header()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
resetFixedColumns();
|
||||
fillRemainingWidth(false);
|
||||
emit viewStateChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset view to defaults
|
||||
* Mark icon-only columns as fixed and resize them to their minimum section size.
|
||||
*/
|
||||
void EntryView::resetFixedColumns()
|
||||
{
|
||||
header()->setSectionResizeMode(EntryModel::Paperclip, QHeaderView::Fixed);
|
||||
header()->resizeSection(EntryModel::Paperclip, header()->minimumSectionSize());
|
||||
|
||||
header()->setSectionResizeMode(EntryModel::Totp, QHeaderView::Fixed);
|
||||
header()->resizeSection(EntryModel::Totp, header()->minimumSectionSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset item view to defaults.
|
||||
*/
|
||||
void EntryView::resetViewToDefaults()
|
||||
{
|
||||
m_model->setUsernamesHidden(false);
|
||||
m_model->setPasswordsHidden(true);
|
||||
|
||||
// Reduce number of columns that are shown by default
|
||||
if (m_inSearchMode) {
|
||||
header()->restoreState(m_defaultSearchViewState);
|
||||
header()->showSection(EntryModel::ParentGroup);
|
||||
} else {
|
||||
header()->restoreState(m_defaultListViewState);
|
||||
header()->hideSection(EntryModel::ParentGroup);
|
||||
}
|
||||
header()->showSection(EntryModel::Title);
|
||||
header()->showSection(EntryModel::Username);
|
||||
header()->showSection(EntryModel::Url);
|
||||
header()->showSection(EntryModel::Notes);
|
||||
header()->showSection(EntryModel::Modified);
|
||||
header()->showSection(EntryModel::Paperclip);
|
||||
header()->showSection(EntryModel::Totp);
|
||||
|
||||
header()->hideSection(EntryModel::Password);
|
||||
header()->hideSection(EntryModel::Expires);
|
||||
header()->hideSection(EntryModel::Created);
|
||||
header()->hideSection(EntryModel::Accessed);
|
||||
header()->hideSection(EntryModel::Attachments);
|
||||
|
||||
// Reset column order to logical indices
|
||||
for (int i = 0; i < header()->count(); ++i) {
|
||||
header()->moveSection(header()->visualIndex(i), i);
|
||||
}
|
||||
|
||||
fitColumnsToWindow();
|
||||
// Reorder some columns
|
||||
header()->moveSection(header()->visualIndex(EntryModel::Paperclip), 1);
|
||||
header()->moveSection(header()->visualIndex(EntryModel::Totp), 2);
|
||||
|
||||
// Sort by title or group (depending on the mode)
|
||||
m_sortModel->sort(EntryModel::Title, Qt::AscendingOrder);
|
||||
sortByColumn(EntryModel::Title, Qt::AscendingOrder);
|
||||
|
||||
if (m_inSearchMode) {
|
||||
m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder);
|
||||
sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
// The following call only relayouts reliably if the widget has been shown
|
||||
// already, so only do it if the widget is visible and let showEvent() handle
|
||||
// the initial default layout.
|
||||
if (isVisible()) {
|
||||
fitColumnsToWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void EntryView::fillRemainingWidth(bool lastColumnOnly)
|
||||
void EntryView::showEvent(QShowEvent* event)
|
||||
{
|
||||
// Determine total width of currently visible columns
|
||||
int width = 0;
|
||||
int lastColumnIndex = 0;
|
||||
for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) {
|
||||
if (!header()->isSectionHidden(columnIndex)) {
|
||||
width += header()->sectionSize(columnIndex);
|
||||
}
|
||||
if (header()->visualIndex(columnIndex) > lastColumnIndex) {
|
||||
lastColumnIndex = header()->visualIndex(columnIndex);
|
||||
}
|
||||
}
|
||||
QTreeView::showEvent(event);
|
||||
|
||||
int numColumns = header()->count() - header()->hiddenSectionCount();
|
||||
int availWidth = header()->width() - width;
|
||||
if ((numColumns <= 0) || (availWidth <= 0)) {
|
||||
return;
|
||||
// Check if header columns need to be resized to sensible defaults.
|
||||
// This is only needed if no previous view state has been loaded.
|
||||
if (m_columnsNeedRelayout) {
|
||||
fitColumnsToWindow();
|
||||
m_columnsNeedRelayout = false;
|
||||
}
|
||||
|
||||
if (!lastColumnOnly) {
|
||||
// Equally distribute remaining width to visible columns
|
||||
int add = availWidth / numColumns;
|
||||
width = 0;
|
||||
for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) {
|
||||
if (!header()->isSectionHidden(columnIndex)) {
|
||||
header()->resizeSection(columnIndex, header()->sectionSize(columnIndex) + add);
|
||||
width += header()->sectionSize(columnIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add remaining width to last column
|
||||
header()->resizeSection(header()->logicalIndex(lastColumnIndex),
|
||||
header()->sectionSize(lastColumnIndex) + (header()->width() - width));
|
||||
}
|
||||
|
||||
void EntryView::resetFixedColumns()
|
||||
{
|
||||
header()->setSectionResizeMode(EntryModel::Paperclip, QHeaderView::Fixed);
|
||||
header()->resizeSection(EntryModel::Paperclip, header()->minimumSectionSize());
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void focusInEvent(QFocusEvent* event) override;
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void emitEntryActivated(const QModelIndex& index);
|
||||
@ -75,15 +76,12 @@ private slots:
|
||||
void contextMenuShortcutPressed();
|
||||
|
||||
private:
|
||||
void fillRemainingWidth(bool lastColumnOnly);
|
||||
void resetFixedColumns();
|
||||
|
||||
EntryModel* const m_model;
|
||||
SortFilterHideProxyModel* const m_sortModel;
|
||||
bool m_inSearchMode;
|
||||
|
||||
QByteArray m_defaultListViewState;
|
||||
QByteArray m_defaultSearchViewState;
|
||||
bool m_columnsNeedRelayout = true;
|
||||
|
||||
QMenu* m_headerMenu;
|
||||
QAction* m_hideUsernamesAction;
|
||||
|
@ -22,7 +22,7 @@
|
||||
QPointer<MacUtils> MacUtils::m_instance = nullptr;
|
||||
|
||||
MacUtils::MacUtils(QObject* parent)
|
||||
: OSUtils(parent)
|
||||
: OSUtilsBase(parent)
|
||||
, m_appkit(new AppKit())
|
||||
{
|
||||
connect(m_appkit.data(), SIGNAL(lockDatabases()), SIGNAL(lockDatabases()));
|
||||
|
@ -21,9 +21,12 @@
|
||||
|
||||
#include "gui/osutils/OSUtilsBase.h"
|
||||
#include "AppKit.h"
|
||||
#include <QPointer>
|
||||
|
||||
class MacUtils : public OSUtils
|
||||
#include <QPointer>
|
||||
#include <QScopedPointer>
|
||||
#include <qwindowdefs.h>
|
||||
|
||||
class MacUtils : public OSUtilsBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
4779
src/gui/styles/base/BaseStyle.cpp
Normal file
4779
src/gui/styles/base/BaseStyle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
101
src/gui/styles/base/BaseStyle.h
Normal file
101
src/gui/styles/base/BaseStyle.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2019 Andrew Richards
|
||||
*
|
||||
* Derived from Phantomstyle and relicensed under the GPLv2 or v3.
|
||||
* https://github.com/randrew/phantomstyle
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_BASESTYLE_H
|
||||
#define KEEPASSXC_BASESTYLE_H
|
||||
|
||||
#include <QCommonStyle>
|
||||
|
||||
class BaseStylePrivate;
|
||||
|
||||
class BaseStyle : public QCommonStyle
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BaseStyle();
|
||||
~BaseStyle() override;
|
||||
|
||||
enum PhantomPrimitiveElement
|
||||
{
|
||||
Phantom_PE_IndicatorTabNew = PE_CustomBase + 1,
|
||||
Phantom_PE_ScrollBarSliderVertical,
|
||||
Phantom_PE_WindowFrameColor,
|
||||
};
|
||||
|
||||
QPalette standardPalette() const override;
|
||||
void drawPrimitive(PrimitiveElement elem,
|
||||
const QStyleOption* option,
|
||||
QPainter* painter,
|
||||
const QWidget* widget = nullptr) const override;
|
||||
void
|
||||
drawControl(ControlElement ce, const QStyleOption* option, QPainter* painter, const QWidget* widget) const override;
|
||||
int pixelMetric(PixelMetric metric,
|
||||
const QStyleOption* option = nullptr,
|
||||
const QWidget* widget = nullptr) const override;
|
||||
void drawComplexControl(ComplexControl control,
|
||||
const QStyleOptionComplex* option,
|
||||
QPainter* painter,
|
||||
const QWidget* widget) const override;
|
||||
QRect subElementRect(SubElement r, const QStyleOption* opt, const QWidget* widget = nullptr) const override;
|
||||
QSize sizeFromContents(ContentsType type,
|
||||
const QStyleOption* option,
|
||||
const QSize& size,
|
||||
const QWidget* widget) const override;
|
||||
SubControl hitTestComplexControl(ComplexControl cc,
|
||||
const QStyleOptionComplex* opt,
|
||||
const QPoint& pt,
|
||||
const QWidget* w = nullptr) const override;
|
||||
QRect subControlRect(ComplexControl cc,
|
||||
const QStyleOptionComplex* opt,
|
||||
SubControl sc,
|
||||
const QWidget* widget) const override;
|
||||
QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap, const QStyleOption* opt) const override;
|
||||
int styleHint(StyleHint hint,
|
||||
const QStyleOption* option = nullptr,
|
||||
const QWidget* widget = nullptr,
|
||||
QStyleHintReturn* returnData = nullptr) const override;
|
||||
QRect itemPixmapRect(const QRect& r, int flags, const QPixmap& pixmap) const override;
|
||||
void drawItemPixmap(QPainter* painter, const QRect& rect, int alignment, const QPixmap& pixmap) const override;
|
||||
void drawItemText(QPainter* painter,
|
||||
const QRect& rect,
|
||||
int flags,
|
||||
const QPalette& pal,
|
||||
bool enabled,
|
||||
const QString& text,
|
||||
QPalette::ColorRole textRole = QPalette::NoRole) const override;
|
||||
|
||||
using QCommonStyle::polish;
|
||||
void polish(QApplication* app) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return Paths to application stylesheets
|
||||
*/
|
||||
virtual QString getAppStyleSheet() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
BaseStylePrivate* d;
|
||||
};
|
||||
|
||||
#endif
|
48
src/gui/styles/base/basestyle.qss
Normal file
48
src/gui/styles/base/basestyle.qss
Normal file
@ -0,0 +1,48 @@
|
||||
QPushButton:default {
|
||||
background: palette(highlight);
|
||||
color: palette(highlighted-text);
|
||||
}
|
||||
|
||||
QSpinBox {
|
||||
min-width: 90px;
|
||||
}
|
||||
|
||||
QDialogButtonBox QPushButton {
|
||||
min-width: 55px;
|
||||
}
|
||||
|
||||
QCheckBox, QRadioButton {
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
DatabaseWidget, GroupView {
|
||||
background-color: palette(window);
|
||||
border: none;
|
||||
}
|
||||
|
||||
EntryPreviewWidget QLineEdit, EntryPreviewWidget QTextEdit {
|
||||
background-color: palette(window);
|
||||
border: none;
|
||||
}
|
||||
|
||||
DatabaseOpenWidget #loginFrame {
|
||||
border: 2px groove palette(mid);
|
||||
background: palette(light);
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
margin-top: 1.4em;
|
||||
margin-bottom: 1.4em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
margin-top: -3.4em;
|
||||
margin-left: -.4em;
|
||||
subcontrol-origin: padding;
|
||||
}
|
||||
|
||||
QToolTip {
|
||||
border: none;
|
||||
padding: 3px;
|
||||
}
|
423
src/gui/styles/base/phantomcolor.cpp
Normal file
423
src/gui/styles/base/phantomcolor.cpp
Normal file
@ -0,0 +1,423 @@
|
||||
/*
|
||||
* HSLuv-C: Human-friendly HSL
|
||||
* <http://github.com/hsluv/hsluv-c>
|
||||
* <http://www.hsluv.org/>
|
||||
*
|
||||
* Copyright (c) 2015 Alexei Boronine (original idea, JavaScript implementation)
|
||||
* Copyright (c) 2015 Roger Tallada (Obj-C implementation)
|
||||
* Copyright (c) 2017 Martin Mitas (C implementation, based on Obj-C implementation)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "phantomcolor.h"
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
namespace Phantom
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
// Th`ese declarations originate from hsluv.h, from the hsluv-c library. The
|
||||
// hpluv functions have been removed, as they are unnecessary for Phantom.
|
||||
/**
|
||||
* Convert HSLuv to RGB.
|
||||
*
|
||||
* @param h Hue. Between 0.0 and 360.0.
|
||||
* @param s Saturation. Between 0.0 and 100.0.
|
||||
* @param l Lightness. Between 0.0 and 100.0.
|
||||
* @param[out] pr Red component. Between 0.0 and 1.0.
|
||||
* @param[out] pr Green component. Between 0.0 and 1.0.
|
||||
* @param[out] pr Blue component. Between 0.0 and 1.0.
|
||||
*/
|
||||
void hsluv2rgb(double h, double s, double l, double* pr, double* pg, double* pb);
|
||||
|
||||
/**
|
||||
* Convert RGB to HSLuv.
|
||||
*
|
||||
* @param r Red component. Between 0.0 and 1.0.
|
||||
* @param g Green component. Between 0.0 and 1.0.
|
||||
* @param b Blue component. Between 0.0 and 1.0.
|
||||
* @param[out] ph Hue. Between 0.0 and 360.0.
|
||||
* @param[out] ps Saturation. Between 0.0 and 100.0.
|
||||
* @param[out] pl Lightness. Between 0.0 and 100.0.
|
||||
*/
|
||||
void rgb2hsluv(double r, double g, double b, double* ph, double* ps, double* pl);
|
||||
|
||||
// Contents below originate from hsluv.c from the hsluv-c library. They have
|
||||
// been wrapped in a C++ namespace to avoid collisions and to reduce the
|
||||
// translation unit count, and hsluv's own sRGB conversion code has been
|
||||
// stripped out (sRGB conversion is now performed in the Phantom color code
|
||||
// when going to/from the Rgb type.)
|
||||
//
|
||||
// If you need to update the hsluv-c code, be mindful of the removed sRGB
|
||||
// conversions -- you will need to make similar modifications to the upstream
|
||||
// hsluv-c code. Also note that that the hpluv (pastel) functions have been
|
||||
// removed, as they are not used in Phantom.
|
||||
typedef struct Triplet_tag Triplet;
|
||||
struct Triplet_tag
|
||||
{
|
||||
double a;
|
||||
double b;
|
||||
double c;
|
||||
};
|
||||
|
||||
/* for RGB */
|
||||
const Triplet m[3] = {{3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366},
|
||||
{-0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247},
|
||||
{0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072}};
|
||||
|
||||
/* for XYZ */
|
||||
const Triplet m_inv[3] = {{0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751},
|
||||
{0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500},
|
||||
{0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086}};
|
||||
|
||||
const double ref_u = 0.19783000664283680764;
|
||||
const double ref_v = 0.46831999493879100370;
|
||||
|
||||
const double kappa = 903.29629629629629629630;
|
||||
const double epsilon = 0.00885645167903563082;
|
||||
|
||||
typedef struct Bounds_tag Bounds;
|
||||
struct Bounds_tag
|
||||
{
|
||||
double a;
|
||||
double b;
|
||||
};
|
||||
|
||||
void get_bounds(double l, Bounds bounds[6])
|
||||
{
|
||||
double tl = l + 16.0;
|
||||
double sub1 = (tl * tl * tl) / 1560896.0;
|
||||
double sub2 = (sub1 > epsilon ? sub1 : (l / kappa));
|
||||
int channel;
|
||||
int t;
|
||||
|
||||
for (channel = 0; channel < 3; channel++) {
|
||||
double m1 = m[channel].a;
|
||||
double m2 = m[channel].b;
|
||||
double m3 = m[channel].c;
|
||||
|
||||
for (t = 0; t < 2; t++) {
|
||||
double top1 = (284517.0 * m1 - 94839.0 * m3) * sub2;
|
||||
double top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * t * l;
|
||||
double bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * t;
|
||||
|
||||
bounds[channel * 2 + t].a = top1 / bottom;
|
||||
bounds[channel * 2 + t].b = top2 / bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double ray_length_until_intersect(double theta, const Bounds* line)
|
||||
{
|
||||
return line->b / (sin(theta) - line->a * cos(theta));
|
||||
}
|
||||
|
||||
double max_chroma_for_lh(double l, double h)
|
||||
{
|
||||
double min_len = DBL_MAX;
|
||||
double hrad = h * 0.01745329251994329577; /* (2 * pi / 360) */
|
||||
Bounds bounds[6];
|
||||
int i;
|
||||
|
||||
get_bounds(l, bounds);
|
||||
for (i = 0; i < 6; i++) {
|
||||
double len = ray_length_until_intersect(hrad, &bounds[i]);
|
||||
|
||||
if (len >= 0 && len < min_len)
|
||||
min_len = len;
|
||||
}
|
||||
return min_len;
|
||||
}
|
||||
|
||||
double dot_product(const Triplet* t1, const Triplet* t2)
|
||||
{
|
||||
return (t1->a * t2->a + t1->b * t2->b + t1->c * t2->c);
|
||||
}
|
||||
|
||||
void xyz2rgb(Triplet* in_out)
|
||||
{
|
||||
double r = dot_product(&m[0], in_out);
|
||||
double g = dot_product(&m[1], in_out);
|
||||
double b = dot_product(&m[2], in_out);
|
||||
in_out->a = r;
|
||||
in_out->b = g;
|
||||
in_out->c = b;
|
||||
}
|
||||
|
||||
void rgb2xyz(Triplet* in_out)
|
||||
{
|
||||
Triplet rgbl = {in_out->a, in_out->b, in_out->c};
|
||||
double x = dot_product(&m_inv[0], &rgbl);
|
||||
double y = dot_product(&m_inv[1], &rgbl);
|
||||
double z = dot_product(&m_inv[2], &rgbl);
|
||||
in_out->a = x;
|
||||
in_out->b = y;
|
||||
in_out->c = z;
|
||||
}
|
||||
|
||||
/* http://en.wikipedia.org/wiki/CIELUV
|
||||
* In these formulas, Yn refers to the reference white point. We are using
|
||||
* illuminant D65, so Yn (see refY in Maxima file) equals 1. The formula is
|
||||
* simplified accordingly.
|
||||
*/
|
||||
double y2l(double y)
|
||||
{
|
||||
if (y <= epsilon) {
|
||||
return y * kappa;
|
||||
} else {
|
||||
return 116.0 * cbrt(y) - 16.0;
|
||||
}
|
||||
}
|
||||
|
||||
double l2y(double l)
|
||||
{
|
||||
if (l <= 8.0) {
|
||||
return l / kappa;
|
||||
} else {
|
||||
double x = (l + 16.0) / 116.0;
|
||||
return (x * x * x);
|
||||
}
|
||||
}
|
||||
|
||||
void xyz2luv(Triplet* in_out)
|
||||
{
|
||||
double divisor = in_out->a + (15.0 * in_out->b) + (3.0 * in_out->c);
|
||||
if (divisor <= 0.00000001) {
|
||||
in_out->a = 0.0;
|
||||
in_out->b = 0.0;
|
||||
in_out->c = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
double var_u = (4.0 * in_out->a) / divisor;
|
||||
double var_v = (9.0 * in_out->b) / divisor;
|
||||
double l = y2l(in_out->b);
|
||||
double u = 13.0 * l * (var_u - ref_u);
|
||||
double v = 13.0 * l * (var_v - ref_v);
|
||||
|
||||
in_out->a = l;
|
||||
if (l < 0.00000001) {
|
||||
in_out->b = 0.0;
|
||||
in_out->c = 0.0;
|
||||
} else {
|
||||
in_out->b = u;
|
||||
in_out->c = v;
|
||||
}
|
||||
}
|
||||
|
||||
void luv2xyz(Triplet* in_out)
|
||||
{
|
||||
if (in_out->a <= 0.00000001) {
|
||||
/* Black will create a divide-by-zero error. */
|
||||
in_out->a = 0.0;
|
||||
in_out->b = 0.0;
|
||||
in_out->c = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
double var_u = in_out->b / (13.0 * in_out->a) + ref_u;
|
||||
double var_v = in_out->c / (13.0 * in_out->a) + ref_v;
|
||||
double y = l2y(in_out->a);
|
||||
double x = -(9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v);
|
||||
double z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v);
|
||||
in_out->a = x;
|
||||
in_out->b = y;
|
||||
in_out->c = z;
|
||||
}
|
||||
|
||||
void luv2lch(Triplet* in_out)
|
||||
{
|
||||
double l = in_out->a;
|
||||
double u = in_out->b;
|
||||
double v = in_out->c;
|
||||
double h;
|
||||
double c = sqrt(u * u + v * v);
|
||||
|
||||
/* Grays: disambiguate hue */
|
||||
if (c < 0.00000001) {
|
||||
h = 0;
|
||||
} else {
|
||||
h = atan2(v, u) * 57.29577951308232087680; /* (180 / pi) */
|
||||
if (h < 0.0)
|
||||
h += 360.0;
|
||||
}
|
||||
|
||||
in_out->a = l;
|
||||
in_out->b = c;
|
||||
in_out->c = h;
|
||||
}
|
||||
|
||||
void lch2luv(Triplet* in_out)
|
||||
{
|
||||
double hrad = in_out->c * 0.01745329251994329577; /* (pi / 180.0) */
|
||||
double u = cos(hrad) * in_out->b;
|
||||
double v = sin(hrad) * in_out->b;
|
||||
|
||||
in_out->b = u;
|
||||
in_out->c = v;
|
||||
}
|
||||
|
||||
void hsluv2lch(Triplet* in_out)
|
||||
{
|
||||
double h = in_out->a;
|
||||
double s = in_out->b;
|
||||
double l = in_out->c;
|
||||
double c;
|
||||
|
||||
/* White and black: disambiguate chroma */
|
||||
if (l > 99.9999999 || l < 0.00000001) {
|
||||
c = 0.0;
|
||||
} else {
|
||||
c = max_chroma_for_lh(l, h) / 100.0 * s;
|
||||
}
|
||||
|
||||
/* Grays: disambiguate hue */
|
||||
if (s < 0.00000001)
|
||||
h = 0.0;
|
||||
|
||||
in_out->a = l;
|
||||
in_out->b = c;
|
||||
in_out->c = h;
|
||||
}
|
||||
|
||||
void lch2hsluv(Triplet* in_out)
|
||||
{
|
||||
double l = in_out->a;
|
||||
double c = in_out->b;
|
||||
double h = in_out->c;
|
||||
double s;
|
||||
|
||||
/* White and black: disambiguate saturation */
|
||||
if (l > 99.9999999 || l < 0.00000001) {
|
||||
s = 0.0;
|
||||
} else {
|
||||
s = c / max_chroma_for_lh(l, h) * 100.0;
|
||||
}
|
||||
|
||||
/* Grays: disambiguate hue */
|
||||
if (c < 0.00000001)
|
||||
h = 0.0;
|
||||
|
||||
in_out->a = h;
|
||||
in_out->b = s;
|
||||
in_out->c = l;
|
||||
}
|
||||
|
||||
void hsluv2rgb(double h, double s, double l, double* pr, double* pg, double* pb)
|
||||
{
|
||||
Triplet tmp = {h, s, l};
|
||||
|
||||
hsluv2lch(&tmp);
|
||||
lch2luv(&tmp);
|
||||
luv2xyz(&tmp);
|
||||
xyz2rgb(&tmp);
|
||||
|
||||
*pr = tmp.a;
|
||||
*pg = tmp.b;
|
||||
*pb = tmp.c;
|
||||
}
|
||||
|
||||
void rgb2hsluv(double r, double g, double b, double* ph, double* ps, double* pl)
|
||||
{
|
||||
Triplet tmp = {r, g, b};
|
||||
|
||||
rgb2xyz(&tmp);
|
||||
xyz2luv(&tmp);
|
||||
luv2lch(&tmp);
|
||||
lch2hsluv(&tmp);
|
||||
|
||||
*ph = tmp.a;
|
||||
*ps = tmp.b;
|
||||
*pl = tmp.c;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Phantom
|
||||
|
||||
// The code below is for Phantom, and is used for the Rgb/Hsl-based interface
|
||||
// for color operations.
|
||||
namespace Phantom
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Note: these constants might be out of range when qreal is defined as float
|
||||
// instead of double.
|
||||
inline qreal linear_of_srgb(qreal x)
|
||||
{
|
||||
return x < 0.0404482362771082 ? x / 12.92 : std::pow((x + 0.055) / 1.055, 2.4f);
|
||||
}
|
||||
inline qreal srgb_of_linear(qreal x)
|
||||
{
|
||||
return x < 0.00313066844250063 ? x * 12.92 : std::pow(x, 1.0 / 2.4) * 1.055 - 0.055;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Rgb rgb_of_qcolor(const QColor& color)
|
||||
{
|
||||
Rgb a;
|
||||
a.r = linear_of_srgb(color.red() / 255.0);
|
||||
a.g = linear_of_srgb(color.green() / 255.0);
|
||||
a.b = linear_of_srgb(color.blue() / 255.0);
|
||||
return a;
|
||||
}
|
||||
|
||||
Hsl hsl_of_rgb(qreal r, qreal g, qreal b)
|
||||
{
|
||||
double h, s, l;
|
||||
rgb2hsluv(r, g, b, &h, &s, &l);
|
||||
s /= 100.0;
|
||||
l /= 100.0;
|
||||
return {h, s, l};
|
||||
}
|
||||
|
||||
Rgb rgb_of_hsl(qreal h, qreal s, qreal l)
|
||||
{
|
||||
double r, g, b;
|
||||
hsluv2rgb(h, s * 100.0, l * 100.0, &r, &g, &b);
|
||||
return {r, g, b};
|
||||
}
|
||||
|
||||
QColor qcolor_of_rgb(qreal r, qreal g, qreal b)
|
||||
{
|
||||
int r_ = static_cast<int>(std::lround(srgb_of_linear(r) * 255.0));
|
||||
int g_ = static_cast<int>(std::lround(srgb_of_linear(g) * 255.0));
|
||||
int b_ = static_cast<int>(std::lround(srgb_of_linear(b) * 255.0));
|
||||
return {r_, g_, b_};
|
||||
}
|
||||
|
||||
QColor lerpQColor(const QColor& x, const QColor& y, qreal a)
|
||||
{
|
||||
Rgb x_ = rgb_of_qcolor(x);
|
||||
Rgb y_ = rgb_of_qcolor(y);
|
||||
Rgb z = Rgb::lerp(x_, y_, a);
|
||||
return qcolor_of_rgb(z.r, z.g, z.b);
|
||||
}
|
||||
|
||||
Rgb Rgb::lerp(const Rgb& x, const Rgb& y, qreal a)
|
||||
{
|
||||
Rgb z;
|
||||
z.r = (1.0 - a) * x.r + a * y.r;
|
||||
z.g = (1.0 - a) * x.g + a * y.g;
|
||||
z.b = (1.0 - a) * x.b + a * y.b;
|
||||
return z;
|
||||
}
|
||||
} // namespace Phantom
|
165
src/gui/styles/base/phantomcolor.h
Normal file
165
src/gui/styles/base/phantomcolor.h
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* HSLuv-C: Human-friendly HSL
|
||||
* <http://github.com/hsluv/hsluv-c>
|
||||
* <http://www.hsluv.org/>
|
||||
*
|
||||
* Copyright (c) 2015 Alexei Boronine (original idea, JavaScript implementation)
|
||||
* Copyright (c) 2015 Roger Tallada (Obj-C implementation)
|
||||
* Copyright (c) 2017 Martin Mitas (C implementation, based on Obj-C implementation)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PHANTOMCOLOR_H
|
||||
#define PHANTOMCOLOR_H
|
||||
|
||||
#include <QColor>
|
||||
|
||||
namespace Phantom
|
||||
{
|
||||
struct Rgb;
|
||||
struct Hsl;
|
||||
|
||||
// A color presumed to be in linear space, represented as RGB. Values are in
|
||||
// the range 0.0 - 1.0. Conversions to and from QColor will assume the QColor
|
||||
// is in sRGB space, and sRGB conversion will be performed.
|
||||
struct Rgb
|
||||
{
|
||||
qreal r, g, b;
|
||||
Rgb()
|
||||
{
|
||||
}
|
||||
Rgb(qreal r, qreal g, qreal b)
|
||||
: r(r)
|
||||
, g(g)
|
||||
, b(b)
|
||||
{
|
||||
}
|
||||
|
||||
inline Hsl toHsl() const;
|
||||
inline QColor toQColor() const;
|
||||
static inline Rgb ofHsl(const Hsl&);
|
||||
static inline Rgb ofQColor(const QColor&);
|
||||
|
||||
static Rgb lerp(const Rgb& x, const Rgb& y, qreal a);
|
||||
};
|
||||
|
||||
// A color represented as pseudo-CIE hue, saturation, and lightness. Hue is in
|
||||
// the range 0.0 - 360.0 (degrees). Lightness and saturation are in the range
|
||||
// 0.0 - 1.0. Using this and making adjustments to the L value will produce
|
||||
// more consistent and predictable results than QColor's .darker()/.lighter().
|
||||
// Note that this is not strictly CIE -- some of the colorspace is distorted so
|
||||
// that it can represented as a continuous coordinate space. Therefore not all
|
||||
// adjustments to the parameters will produce perfectly linear results with
|
||||
// regards to saturation and lightness. But it's still useful, and better than
|
||||
// QColor's .darker()/.lighter(). Additionally, the L value is more useful for
|
||||
// performing comparisons between two colors to measure relative and absolute
|
||||
// brightness.
|
||||
//
|
||||
// See the documentation for the hsluv library for more information. (Note that
|
||||
// for consistency we treat the S and L values in the range 0.0 - 1.0 instead
|
||||
// of 0.0 - 100.0 like hsluv-c on its own does.)
|
||||
struct Hsl
|
||||
{
|
||||
qreal h, s, l;
|
||||
Hsl()
|
||||
{
|
||||
}
|
||||
Hsl(qreal h, qreal s, qreal l)
|
||||
: h(h)
|
||||
, s(s)
|
||||
, l(l)
|
||||
{
|
||||
}
|
||||
|
||||
inline Rgb toRgb() const;
|
||||
inline QColor toQColor() const;
|
||||
static inline Hsl ofRgb(const Rgb&);
|
||||
static inline Hsl ofQColor(const QColor&);
|
||||
};
|
||||
Rgb rgb_of_qcolor(const QColor& color);
|
||||
QColor qcolor_of_rgb(qreal r, qreal g, qreal b);
|
||||
Hsl hsl_of_rgb(qreal r, qreal g, qreal b);
|
||||
Rgb rgb_of_hsl(qreal h, qreal s, qreal l);
|
||||
|
||||
// Clip a floating point value to the range 0.0 - 1.0.
|
||||
inline qreal saturate(qreal x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
return 0.0;
|
||||
if (x > 1.0)
|
||||
return 1.0;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline qreal lerp(qreal x, qreal y, qreal a)
|
||||
{
|
||||
return (1.0 - a) * x + a * y;
|
||||
}
|
||||
|
||||
// Linearly interpolate two QColors after trasnforming them to linear color
|
||||
// space, treating the QColor values as if they were in sRGB space. The
|
||||
// returned QColor is converted back to sRGB space.
|
||||
QColor lerpQColor(const QColor& x, const QColor& y, qreal a);
|
||||
|
||||
Hsl Rgb::toHsl() const
|
||||
{
|
||||
return hsl_of_rgb(r, g, b);
|
||||
}
|
||||
|
||||
QColor Rgb::toQColor() const
|
||||
{
|
||||
return qcolor_of_rgb(r, g, b);
|
||||
}
|
||||
|
||||
Rgb Rgb::ofHsl(const Hsl& hsl)
|
||||
{
|
||||
return rgb_of_hsl(hsl.h, hsl.s, hsl.l);
|
||||
}
|
||||
|
||||
Rgb Rgb::ofQColor(const QColor& color)
|
||||
{
|
||||
return rgb_of_qcolor(color);
|
||||
}
|
||||
|
||||
Rgb Hsl::toRgb() const
|
||||
{
|
||||
return rgb_of_hsl(h, s, l);
|
||||
}
|
||||
|
||||
QColor Hsl::toQColor() const
|
||||
{
|
||||
Rgb rgb = rgb_of_hsl(h, s, l);
|
||||
return qcolor_of_rgb(rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
|
||||
Hsl Hsl::ofRgb(const Rgb& rgb)
|
||||
{
|
||||
return hsl_of_rgb(rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
|
||||
Hsl Hsl::ofQColor(const QColor& color)
|
||||
{
|
||||
Rgb rgb = rgb_of_qcolor(color);
|
||||
return hsl_of_rgb(rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
|
||||
} // namespace Phantom
|
||||
|
||||
#endif
|
125
src/gui/styles/dark/DarkStyle.cpp
Normal file
125
src/gui/styles/dark/DarkStyle.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DarkStyle.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QToolBar>
|
||||
|
||||
void DarkStyle::polish(QPalette& palette)
|
||||
{
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QStringLiteral("#3B3B3D"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Window, QStringLiteral("#404042"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QStringLiteral("#424242"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::WindowText, QStringLiteral("#CACBCE"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::WindowText, QStringLiteral("#C8C8C6"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::WindowText, QStringLiteral("#707070"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Text, QStringLiteral("#CACBCE"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Text, QStringLiteral("#C8C8C6"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Text, QStringLiteral("#707070"));
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
palette.setColor(QPalette::Active, QPalette::PlaceholderText, QStringLiteral("#7D7D82"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::PlaceholderText, QStringLiteral("#87888C"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::PlaceholderText, QStringLiteral("#737373"));
|
||||
#endif
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::BrightText, QStringLiteral("#252627"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::BrightText, QStringLiteral("#2D2D2F"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::BrightText, QStringLiteral("#333333"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Base, QStringLiteral("#27272A"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Base, QStringLiteral("#2A2A2D"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Base, QStringLiteral("#343437"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::AlternateBase, QStringLiteral("#303036"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::AlternateBase, QStringLiteral("#333338"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::AlternateBase, QStringLiteral("#36363A"));
|
||||
|
||||
palette.setColor(QPalette::All, QPalette::ToolTipBase, QStringLiteral("#2D532D"));
|
||||
palette.setColor(QPalette::All, QPalette::ToolTipText, QStringLiteral("#BFBFBF"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Button, QStringLiteral("#28282B"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Button, QStringLiteral("#2B2B2E"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Button, QStringLiteral("#2B2A2A"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::ButtonText, QStringLiteral("#B9B9BE"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::ButtonText, QStringLiteral("#9E9FA5"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QStringLiteral("#73747E"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Highlight, QStringLiteral("#2D532D"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Highlight, QStringLiteral("#294C29"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Highlight, QStringLiteral("#293D29"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::HighlightedText, QStringLiteral("#CCCCCC"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::HighlightedText, QStringLiteral("#C7C7C7"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::HighlightedText, QStringLiteral("#707070"));
|
||||
|
||||
palette.setColor(QPalette::All, QPalette::Light, QStringLiteral("#414145"));
|
||||
palette.setColor(QPalette::All, QPalette::Midlight, QStringLiteral("#39393C"));
|
||||
palette.setColor(QPalette::All, QPalette::Mid, QStringLiteral("#2F2F32"));
|
||||
palette.setColor(QPalette::All, QPalette::Dark, QStringLiteral("#202022"));
|
||||
palette.setColor(QPalette::All, QPalette::Shadow, QStringLiteral("#19191A"));
|
||||
|
||||
palette.setColor(QPalette::All, QPalette::Link, QStringLiteral("#6BAE6B"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Link, QStringLiteral("#9DE9D"));
|
||||
palette.setColor(QPalette::All, QPalette::LinkVisited, QStringLiteral("#70A970"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::LinkVisited, QStringLiteral("#98A998"));
|
||||
}
|
||||
|
||||
QString DarkStyle::getAppStyleSheet() const
|
||||
{
|
||||
QFile extStylesheetFile(QStringLiteral(":/styles/dark/darkstyle.qss"));
|
||||
if (extStylesheetFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return extStylesheetFile.readAll();
|
||||
}
|
||||
qWarning("Failed to load dark theme stylesheet.");
|
||||
return {};
|
||||
}
|
||||
|
||||
void DarkStyle::polish(QWidget* widget)
|
||||
{
|
||||
if (qobject_cast<QMainWindow*>(widget) || qobject_cast<QDialog*>(widget) || qobject_cast<QMenuBar*>(widget)
|
||||
|| qobject_cast<QToolBar*>(widget)) {
|
||||
auto palette = widget->palette();
|
||||
#if defined(Q_OS_MACOS)
|
||||
if (osUtils->isDarkMode()) {
|
||||
// Let the Cocoa platform plugin draw its own background
|
||||
palette.setColor(QPalette::All, QPalette::Window, Qt::transparent);
|
||||
} else {
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QStringLiteral("#2A2A2A"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Window, QStringLiteral("#2D2D2D"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QStringLiteral("#2A2A2A"));
|
||||
}
|
||||
#elif defined(Q_OS_WIN)
|
||||
// Register event filter for better dark mode support
|
||||
WinUtils::registerEventFilters();
|
||||
palette.setColor(QPalette::All, QPalette::Window, QStringLiteral("#2F2F30"));
|
||||
#else
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QStringLiteral("#2F2F30"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Window, QStringLiteral("#313133"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QStringLiteral("#3A3A3B"));
|
||||
#endif
|
||||
|
||||
widget->setPalette(palette);
|
||||
}
|
||||
}
|
37
src/gui/styles/dark/DarkStyle.h
Normal file
37
src/gui/styles/dark/DarkStyle.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_DARKSTYLE_H
|
||||
#define KEEPASSXC_DARKSTYLE_H
|
||||
|
||||
#include "gui/styles/base/BaseStyle.h"
|
||||
#include <QApplication>
|
||||
|
||||
class DarkStyle : public BaseStyle
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using BaseStyle::polish;
|
||||
void polish(QPalette& palette) override;
|
||||
void polish(QWidget* widget) override;
|
||||
|
||||
protected:
|
||||
QString getAppStyleSheet() const override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_DARKSTYLE_H
|
18
src/gui/styles/dark/darkstyle.qss
Normal file
18
src/gui/styles/dark/darkstyle.qss
Normal file
@ -0,0 +1,18 @@
|
||||
DatabaseWidget:!active, GroupView:!active,
|
||||
EntryPreviewWidget QLineEdit:!active, EntryPreviewWidget QTextEdit:!active {
|
||||
background-color: #404042;
|
||||
}
|
||||
|
||||
DatabaseWidget:disabled, GroupView:disabled,
|
||||
EntryPreviewWidget QLineEdit:disabled, EntryPreviewWidget QTextEdit:disabled {
|
||||
background-color: #424242;
|
||||
}
|
||||
|
||||
QToolTip {
|
||||
color: #BFBFBF;
|
||||
background-color: #2D532D;
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
background-color: palette(light);
|
||||
}
|
124
src/gui/styles/light/LightStyle.cpp
Normal file
124
src/gui/styles/light/LightStyle.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "LightStyle.h"
|
||||
#include "gui/ApplicationSettingsWidget.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QToolBar>
|
||||
|
||||
void LightStyle::polish(QPalette& palette)
|
||||
{
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QStringLiteral("#F7F7F7"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Window, QStringLiteral("#FCFCFC"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QStringLiteral("#EDEDED"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::WindowText, QStringLiteral("#1D1D20"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::WindowText, QStringLiteral("#252528"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::WindowText, QStringLiteral("#8C8C92"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Text, QStringLiteral("#1D1D20"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Text, QStringLiteral("#252528"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Text, QStringLiteral("#8C8C92"));
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
palette.setColor(QPalette::Active, QPalette::PlaceholderText, QStringLiteral("#71727D"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::PlaceholderText, QStringLiteral("#878893"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::PlaceholderText, QStringLiteral("#A3A4AC"));
|
||||
#endif
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::BrightText, QStringLiteral("#F3F3F4"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::BrightText, QStringLiteral("#EAEAEB"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::BrightText, QStringLiteral("#E4E5E7"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Base, QStringLiteral("#F9F9F9"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Base, QStringLiteral("#F5F5F4"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Base, QStringLiteral("#EFEFF2"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::AlternateBase, QStringLiteral("#ECF3E8"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::AlternateBase, QStringLiteral("#EAF2E6"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::AlternateBase, QStringLiteral("#E1E9DD"));
|
||||
|
||||
palette.setColor(QPalette::All, QPalette::ToolTipBase, QStringLiteral("#548C1D"));
|
||||
palette.setColor(QPalette::All, QPalette::ToolTipText, QStringLiteral("#F7F7F7"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Button, QStringLiteral("#D4D5DD"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Button, QStringLiteral("#DCDCE0"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Button, QStringLiteral("#E5E5E6"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::ButtonText, QStringLiteral("#181A18"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::ButtonText, QStringLiteral("#5F6671"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QStringLiteral("#97979B"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::Highlight, QStringLiteral("#549712"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Highlight, QStringLiteral("#528D16"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Highlight, QStringLiteral("#6F9847"));
|
||||
|
||||
palette.setColor(QPalette::Active, QPalette::HighlightedText, QStringLiteral("#FCFCFC"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::HighlightedText, QStringLiteral("#F2F2F2"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::HighlightedText, QStringLiteral("#D9D9D9"));
|
||||
|
||||
palette.setColor(QPalette::All, QPalette::Light, QStringLiteral("#F9F9F9"));
|
||||
palette.setColor(QPalette::All, QPalette::Midlight, QStringLiteral("#E9E9EB"));
|
||||
palette.setColor(QPalette::All, QPalette::Mid, QStringLiteral("#C9C9CF"));
|
||||
palette.setColor(QPalette::All, QPalette::Dark, QStringLiteral("#BBBBC2"));
|
||||
palette.setColor(QPalette::All, QPalette::Shadow, QStringLiteral("#6C6D79"));
|
||||
|
||||
palette.setColor(QPalette::All, QPalette::Link, QStringLiteral("#429F14"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Link, QStringLiteral("#949F8F"));
|
||||
palette.setColor(QPalette::All, QPalette::LinkVisited, QStringLiteral("#3F8C17"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::LinkVisited, QStringLiteral("#838C7E"));
|
||||
}
|
||||
|
||||
QString LightStyle::getAppStyleSheet() const
|
||||
{
|
||||
QFile extStylesheetFile(QStringLiteral(":/styles/light/lightstyle.qss"));
|
||||
if (extStylesheetFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return extStylesheetFile.readAll();
|
||||
}
|
||||
qWarning("Failed to load light theme stylesheet.");
|
||||
return {};
|
||||
}
|
||||
|
||||
void LightStyle::polish(QWidget* widget)
|
||||
{
|
||||
if (qobject_cast<QMainWindow*>(widget) || qobject_cast<QDialog*>(widget) || qobject_cast<QMenuBar*>(widget)
|
||||
|| qobject_cast<QToolBar*>(widget)) {
|
||||
auto palette = widget->palette();
|
||||
#if defined(Q_OS_MACOS)
|
||||
if (!osUtils->isDarkMode()) {
|
||||
// Let the Cocoa platform plugin draw its own background
|
||||
palette.setColor(QPalette::All, QPalette::Window, Qt::transparent);
|
||||
} else {
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QStringLiteral("#D6D6D6"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Window, QStringLiteral("#F6F6F6"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QStringLiteral("#D4D4D4"));
|
||||
}
|
||||
#elif defined(Q_OS_WIN)
|
||||
palette.setColor(QPalette::All, QPalette::Window, QStringLiteral("#FFFFFF"));
|
||||
#else
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QStringLiteral("#EFF0F1"));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Window, QStringLiteral("#EFF0F1"));
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QStringLiteral("#E1E2E4"));
|
||||
#endif
|
||||
|
||||
widget->setPalette(palette);
|
||||
}
|
||||
}
|
37
src/gui/styles/light/LightStyle.h
Normal file
37
src/gui/styles/light/LightStyle.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_LIGHTSTYLE_H
|
||||
#define KEEPASSXC_LIGHTSTYLE_H
|
||||
|
||||
#include "gui/styles/base/BaseStyle.h"
|
||||
#include <QApplication>
|
||||
|
||||
class LightStyle : public BaseStyle
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using BaseStyle::polish;
|
||||
void polish(QPalette& palette) override;
|
||||
void polish(QWidget* widget) override;
|
||||
|
||||
protected:
|
||||
QString getAppStyleSheet() const override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_LIGHTSTYLE_H
|
18
src/gui/styles/light/lightstyle.qss
Normal file
18
src/gui/styles/light/lightstyle.qss
Normal file
@ -0,0 +1,18 @@
|
||||
DatabaseWidget:!active, GroupView:!active,
|
||||
EntryPreviewWidget QLineEdit:!active, EntryPreviewWidget QTextEdit:!active {
|
||||
background-color: #FCFCFC;
|
||||
}
|
||||
|
||||
DatabaseWidget:disabled, GroupView:disabled,
|
||||
EntryPreviewWidget QLineEdit:disabled, EntryPreviewWidget QTextEdit:disabled {
|
||||
background-color: #EDEDED;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
color: palette(highlight);
|
||||
}
|
||||
|
||||
QToolTip {
|
||||
color: #F7F7F7;
|
||||
background-color: #548C1D;
|
||||
}
|
8
src/gui/styles/styles.qrc
Normal file
8
src/gui/styles/styles.qrc
Normal file
@ -0,0 +1,8 @@
|
||||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/styles">
|
||||
<file>base/basestyle.qss</file>
|
||||
<file>dark/darkstyle.qss</file>
|
||||
<file>light/lightstyle.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
17
src/main.cpp
17
src/main.cpp
@ -29,6 +29,9 @@
|
||||
#include "gui/Application.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
#include "gui/styles/dark/DarkStyle.h"
|
||||
#include "gui/styles/light/LightStyle.h"
|
||||
|
||||
#if defined(WITH_ASAN) && defined(WITH_LSAN)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
@ -60,6 +63,20 @@ int main(int argc, char** argv)
|
||||
Application app(argc, argv);
|
||||
Application::setApplicationName("KeePassXC");
|
||||
Application::setApplicationVersion(KEEPASSXC_VERSION);
|
||||
|
||||
QString appTheme = config()->get("GUI/ApplicationTheme").toString();
|
||||
if (appTheme == "auto") {
|
||||
if (osUtils->isDarkMode()) {
|
||||
QApplication::setStyle(new DarkStyle);
|
||||
} else {
|
||||
QApplication::setStyle(new LightStyle);
|
||||
}
|
||||
} else if (appTheme == "light") {
|
||||
QApplication::setStyle(new LightStyle);
|
||||
} else if (appTheme == "dark") {
|
||||
QApplication::setStyle(new DarkStyle);
|
||||
}
|
||||
|
||||
// don't set organizationName as that changes the return value of
|
||||
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
|
||||
Bootstrap::bootstrapApplication();
|
||||
|
Loading…
Reference in New Issue
Block a user