mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Implement Caps Lock warning
This commit is contained in:
parent
596d2cf425
commit
d9214db404
@ -350,7 +350,8 @@ if(HAIKU)
|
||||
target_link_libraries(keepassx_core network)
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(keepassx_core Qt5::DBus)
|
||||
target_link_libraries(keepassx_core Qt5::DBus X11)
|
||||
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(MINGW)
|
||||
target_link_libraries(keepassx_core Wtsapi32.lib Ws2_32.lib)
|
||||
|
@ -115,11 +115,11 @@ QIcon Resources::trayIconUnlocked()
|
||||
return useDarkIcon() ? icon("keepassxc-dark", false) : icon("keepassxc-unlocked", false);
|
||||
}
|
||||
|
||||
QIcon Resources::icon(const QString& name, bool recolor)
|
||||
QIcon Resources::icon(const QString& name, bool recolor, const QColor& overrideColor)
|
||||
{
|
||||
QIcon icon = m_iconCache.value(name);
|
||||
|
||||
if (!icon.isNull()) {
|
||||
if (!icon.isNull() && !overrideColor.isValid()) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@ -128,28 +128,36 @@ QIcon Resources::icon(const QString& name, bool recolor)
|
||||
QImage img = icon.pixmap(128, 128).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
icon = {};
|
||||
|
||||
QPalette palette = getMainWindow()->palette();
|
||||
QPainter painter(&img);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
|
||||
|
||||
if (!overrideColor.isValid()) {
|
||||
QPalette palette = getMainWindow()->palette();
|
||||
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Normal, QPalette::WindowText));
|
||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);
|
||||
|
||||
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::ButtonText));
|
||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Active);
|
||||
|
||||
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::HighlightedText));
|
||||
painter.fillRect(
|
||||
0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::HighlightedText));
|
||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected);
|
||||
|
||||
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Disabled, QPalette::WindowText));
|
||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Disabled);
|
||||
} else {
|
||||
painter.fillRect(0, 0, img.width(), img.height(), overrideColor);
|
||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
|
||||
icon.setIsMask(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!overrideColor.isValid()) {
|
||||
m_iconCache.insert(name, icon);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef KEEPASSX_RESOURCES_H
|
||||
#define KEEPASSX_RESOURCES_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QHash>
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
@ -33,7 +34,7 @@ public:
|
||||
QIcon trayIcon();
|
||||
QIcon trayIconLocked();
|
||||
QIcon trayIconUnlocked();
|
||||
QIcon icon(const QString& name, bool recolor = true);
|
||||
QIcon icon(const QString& name, bool recolor = true, const QColor& overrideColor = QColor::Invalid);
|
||||
QIcon onOffIcon(const QString& name, bool recolor = true);
|
||||
|
||||
static Resources* instance();
|
||||
|
@ -22,16 +22,14 @@
|
||||
#include "core/Resources.h"
|
||||
#include "gui/Font.h"
|
||||
#include "gui/PasswordGeneratorWidget.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
#include "gui/styles/StateColorPalette.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTimer>
|
||||
#include <QToolTip>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
} // namespace
|
||||
|
||||
PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
@ -70,6 +68,13 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
|
||||
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
|
||||
m_passwordGeneratorAction->setVisible(false);
|
||||
|
||||
m_capslockAction =
|
||||
new QAction(resources()->icon("dialog-warning", true, StateColorPalette().color(StateColorPalette::Error)),
|
||||
tr("Warning: Caps Lock enabled!"),
|
||||
nullptr);
|
||||
addAction(m_capslockAction, QLineEdit::LeadingPosition);
|
||||
m_capslockAction->setVisible(false);
|
||||
}
|
||||
|
||||
void PasswordEdit::setRepeatPartner(PasswordEdit* repeatEdit)
|
||||
@ -165,3 +170,34 @@ void PasswordEdit::autocompletePassword(const QString& password)
|
||||
setText(password);
|
||||
}
|
||||
}
|
||||
|
||||
bool PasswordEdit::event(QEvent* event)
|
||||
{
|
||||
if (isVisible()) {
|
||||
checkCapslockState();
|
||||
}
|
||||
return QLineEdit::event(event);
|
||||
}
|
||||
|
||||
void PasswordEdit::checkCapslockState()
|
||||
{
|
||||
if (m_parentPasswordEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool newCapslockState = osUtils->isCapslockEnabled();
|
||||
if (newCapslockState != m_capslockState) {
|
||||
m_capslockState = newCapslockState;
|
||||
m_capslockAction->setVisible(newCapslockState);
|
||||
|
||||
// Force repaint to avoid rendering glitches of QLineEdit contents
|
||||
repaint();
|
||||
|
||||
emit capslockToggled(m_capslockState);
|
||||
|
||||
if (newCapslockState) {
|
||||
QTimer::singleShot(
|
||||
150, [this]() { QToolTip::showText(mapToGlobal(rect().bottomLeft()), m_capslockAction->text()); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,20 +39,27 @@ public slots:
|
||||
void setShowPassword(bool show);
|
||||
void updateRepeatStatus();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
signals:
|
||||
void capslockToggled(bool capslockOn);
|
||||
|
||||
private slots:
|
||||
void autocompletePassword(const QString& password);
|
||||
void popupPasswordGenerator();
|
||||
void setParentPasswordEdit(PasswordEdit* parent);
|
||||
void checkCapslockState();
|
||||
|
||||
private:
|
||||
QPointer<QAction> m_errorAction;
|
||||
QPointer<QAction> m_correctAction;
|
||||
QPointer<QAction> m_toggleVisibleAction;
|
||||
QPointer<QAction> m_passwordGeneratorAction;
|
||||
QPointer<QAction> m_capslockAction;
|
||||
QPointer<PasswordEdit> m_repeatPasswordEdit;
|
||||
QPointer<PasswordEdit> m_parentPasswordEdit;
|
||||
bool m_sendGeneratorSignal = false;
|
||||
bool m_isRepeatPartner = false;
|
||||
bool m_capslockState = false;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_PASSWORDEDIT_H
|
||||
|
@ -31,6 +31,7 @@ class OSUtilsBase : public QObject
|
||||
|
||||
public:
|
||||
virtual bool isDarkMode() = 0;
|
||||
virtual bool isCapslockEnabled() = 0;
|
||||
|
||||
protected:
|
||||
explicit OSUtilsBase(QObject* parent = nullptr);
|
||||
|
@ -19,6 +19,9 @@
|
||||
#include "MacUtils.h"
|
||||
#include <QApplication>
|
||||
|
||||
#include <CoreGraphics/CGEventSource.h>
|
||||
|
||||
|
||||
QPointer<MacUtils> MacUtils::m_instance = nullptr;
|
||||
|
||||
MacUtils::MacUtils(QObject* parent)
|
||||
@ -85,3 +88,8 @@ bool MacUtils::enableScreenRecording()
|
||||
{
|
||||
return m_appkit->enableScreenRecording();
|
||||
}
|
||||
|
||||
bool MacUtils::isCapslockEnabled()
|
||||
{
|
||||
return (CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) & kCGEventFlagMaskAlphaShift) != 0;
|
||||
}
|
||||
|
@ -33,13 +33,15 @@ class MacUtils : public OSUtilsBase
|
||||
public:
|
||||
static MacUtils* instance();
|
||||
|
||||
bool isDarkMode() override;
|
||||
bool isCapslockEnabled() override;
|
||||
|
||||
WId activeWindow();
|
||||
bool raiseWindow(WId pid);
|
||||
bool raiseLastActiveWindow();
|
||||
bool raiseOwnWindow();
|
||||
bool hideOwnWindow();
|
||||
bool isHidden();
|
||||
bool isDarkMode() override;
|
||||
bool enableAccessibility();
|
||||
bool enableScreenRecording();
|
||||
|
||||
|
@ -18,9 +18,17 @@
|
||||
#include "NixUtils.h"
|
||||
#include <QApplication>
|
||||
#include <QColor>
|
||||
#include <QGuiApplication>
|
||||
#include <QPalette>
|
||||
#include <QStyle>
|
||||
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
// namespace required to avoid name clashes with declarations in XKBlib.h
|
||||
namespace X11
|
||||
{
|
||||
#include <X11/XKBlib.h>
|
||||
}
|
||||
|
||||
QPointer<NixUtils> NixUtils::m_instance = nullptr;
|
||||
|
||||
NixUtils* NixUtils::instance()
|
||||
@ -48,3 +56,24 @@ bool NixUtils::isDarkMode()
|
||||
}
|
||||
return qApp->style()->standardPalette().color(QPalette::Window).toHsl().lightness() < 110;
|
||||
}
|
||||
|
||||
bool NixUtils::isCapslockEnabled()
|
||||
{
|
||||
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
|
||||
auto* display = native->nativeResourceForWindow("display", nullptr);
|
||||
if (!display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString platform = QGuiApplication::platformName();
|
||||
if (platform == "xcb") {
|
||||
unsigned state = 0;
|
||||
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
|
||||
return ((state & 1u) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Wayland
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
static NixUtils* instance();
|
||||
|
||||
bool isDarkMode() override;
|
||||
bool isCapslockEnabled() override;
|
||||
|
||||
private:
|
||||
explicit NixUtils(QObject* parent = nullptr);
|
||||
|
@ -83,3 +83,8 @@ bool WinUtils::isDarkMode()
|
||||
QSettings::NativeFormat);
|
||||
return settings.value("AppsUseLightTheme", 1).toInt() == 0;
|
||||
}
|
||||
|
||||
bool WinUtils::isCapslockEnabled()
|
||||
{
|
||||
return GetKeyState(VK_CAPITAL) == 1;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public:
|
||||
static void registerEventFilters();
|
||||
|
||||
bool isDarkMode() override;
|
||||
bool isCapslockEnabled() override;
|
||||
|
||||
protected:
|
||||
explicit WinUtils(QObject* parent = nullptr);
|
||||
|
Loading…
Reference in New Issue
Block a user