Add Mnemonic attached property
This is to accept Alt+(whatever has & in front in the label) for labels, buttons, actions, and whatever requires a nmenonic. I created an attached property because it is kind of similar to QML’s Keys property.
This commit is contained in:
parent
ca882f992b
commit
268f4329c0
|
@ -5,9 +5,12 @@ qt_add_executable(${PROJECT_NAME}
|
|||
qt_add_qml_module(${PROJECT_NAME}
|
||||
URI Camper
|
||||
VERSION 1.0
|
||||
DEPENDENCIES QtCore
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
SOURCES
|
||||
database.cpp database.h
|
||||
mnemonicattached.cpp mnemonicattached.h
|
||||
QML_FILES
|
||||
ErrorNotification.qml
|
||||
LoginPage.qml
|
||||
|
|
|
@ -10,35 +10,45 @@ Page {
|
|||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
text: qsTr("&User:")
|
||||
id: userLabel
|
||||
|
||||
Mnemonic.label: qsTr("&User:")
|
||||
text: Mnemonic.richTextLabel
|
||||
|
||||
Shortcut {
|
||||
sequence: userLabel.Mnemonic.sequence
|
||||
|
||||
onActivated: function () {
|
||||
user.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: user
|
||||
|
||||
focus: true
|
||||
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /[^s].*/
|
||||
}
|
||||
|
||||
onAccepted: function () {
|
||||
loginAction.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("&Password:")
|
||||
id: passwordLabel
|
||||
|
||||
Mnemonic.label: qsTr("&Password:")
|
||||
text: Mnemonic.richTextLabel
|
||||
|
||||
Shortcut {
|
||||
sequence: passwordLabel.Mnemonic.sequence
|
||||
|
||||
onActivated: function () {
|
||||
password.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: password
|
||||
|
||||
echoMode: TextInput.Password
|
||||
|
||||
onAccepted: function () {
|
||||
loginAction.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
@ -49,8 +59,9 @@ Page {
|
|||
Action {
|
||||
id: loginAction
|
||||
|
||||
enabled: user.acceptableInput
|
||||
text: "&Login"
|
||||
Mnemonic.label: qsTr("Log &in")
|
||||
shortcut: Mnemonic.sequence
|
||||
text: Mnemonic.richTextLabel
|
||||
|
||||
onTriggered: function () {
|
||||
Database.open(user.text, password.text);
|
||||
|
|
|
@ -10,13 +10,21 @@ Page {
|
|||
anchors.fill: parent
|
||||
|
||||
ToolButton {
|
||||
icon.name: "system-log-out"
|
||||
text: qsTr("&Log out")
|
||||
action: logoutAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: function () {
|
||||
Action {
|
||||
id: logoutAction
|
||||
|
||||
Mnemonic.label: qsTr("Log &out")
|
||||
icon.name: "system-log-out"
|
||||
shortcut: Mnemonic.sequence
|
||||
text: Mnemonic.richTextLabel
|
||||
|
||||
onTriggered: function () {
|
||||
Database.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
#include "mnemonicattached.h"
|
||||
#include <QEvent>
|
||||
#include <QGuiApplication>
|
||||
#include <QKeyEvent>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
class MnemonicEventFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static MnemonicEventFilter &instance()
|
||||
{
|
||||
static MnemonicEventFilter s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
bool isAltPressed() const { return m_altPressed; }
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override
|
||||
{
|
||||
Q_UNUSED(watched);
|
||||
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
|
||||
if (ke->key() == Qt::Key_Alt) {
|
||||
m_altPressed = true;
|
||||
emit altPressed();
|
||||
}
|
||||
} else if (event->type() == QEvent::KeyRelease) {
|
||||
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
|
||||
if (ke->key() == Qt::Key_Alt) {
|
||||
m_altPressed = false;
|
||||
emit altReleased();
|
||||
}
|
||||
} else if (event->type() == QEvent::ApplicationStateChange) {
|
||||
if (m_altPressed) {
|
||||
m_altPressed = false;
|
||||
emit altReleased();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void altPressed();
|
||||
void altReleased();
|
||||
|
||||
private:
|
||||
MnemonicEventFilter()
|
||||
: QObject(nullptr)
|
||||
{
|
||||
qGuiApp->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool m_altPressed = false;
|
||||
};
|
||||
|
||||
MnemonicAttached::MnemonicAttached(QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_label{}
|
||||
, m_richTextLabel{}
|
||||
, m_active{MnemonicEventFilter::instance().isAltPressed()}
|
||||
{
|
||||
connect(&MnemonicEventFilter::instance(),
|
||||
&MnemonicEventFilter::altPressed,
|
||||
this,
|
||||
&MnemonicAttached::onAltPressed);
|
||||
connect(&MnemonicEventFilter::instance(),
|
||||
&MnemonicEventFilter::altReleased,
|
||||
this,
|
||||
&MnemonicAttached::onAltReleased);
|
||||
}
|
||||
|
||||
MnemonicAttached *MnemonicAttached::qmlAttachedProperties(QObject *object)
|
||||
{
|
||||
return new MnemonicAttached(object);
|
||||
}
|
||||
|
||||
QString MnemonicAttached::label() const
|
||||
{
|
||||
return m_label;
|
||||
}
|
||||
|
||||
void MnemonicAttached::setLabel(const QString &label)
|
||||
{
|
||||
if (m_label == label) {
|
||||
return;
|
||||
}
|
||||
m_label = label;
|
||||
emit labelChanged();
|
||||
emit sequenceChanged();
|
||||
|
||||
updateRichText();
|
||||
}
|
||||
|
||||
QString MnemonicAttached::richTextLabel() const
|
||||
{
|
||||
return m_richTextLabel;
|
||||
}
|
||||
|
||||
QKeySequence MnemonicAttached::sequence() const
|
||||
{
|
||||
return QKeySequence::mnemonic(m_label);
|
||||
}
|
||||
|
||||
void MnemonicAttached::onAltPressed()
|
||||
{
|
||||
if (m_active) {
|
||||
return;
|
||||
}
|
||||
m_active = true;
|
||||
updateRichText();
|
||||
}
|
||||
|
||||
void MnemonicAttached::onAltReleased()
|
||||
{
|
||||
if (!m_active) {
|
||||
return;
|
||||
}
|
||||
m_active = false;
|
||||
updateRichText();
|
||||
}
|
||||
|
||||
void MnemonicAttached::updateRichText()
|
||||
{
|
||||
QString richTextLabel;
|
||||
qsizetype pos = m_label.indexOf('&'_L1);
|
||||
if (pos < 0 || pos + 1 == m_label.length()) {
|
||||
richTextLabel = m_label;
|
||||
} else if (m_active) {
|
||||
richTextLabel = m_label.left(pos) + "<u>"_L1 + m_label.at(pos + 1) + "</u>"
|
||||
+ m_label.mid(pos + 2);
|
||||
} else {
|
||||
richTextLabel = m_label.left(pos) + m_label.mid(pos + 1);
|
||||
}
|
||||
if (m_richTextLabel == richTextLabel) {
|
||||
return;
|
||||
}
|
||||
m_richTextLabel = richTextLabel;
|
||||
emit richTextLabelChanged();
|
||||
}
|
||||
|
||||
#include "mnemonicattached.moc"
|
||||
#include "moc_mnemonicattached.cpp"
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef MNEMONICATTACHED_H
|
||||
#define MNEMONICATTACHED_H
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
class MnemonicAttached : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged FINAL)
|
||||
Q_PROPERTY(QString richTextLabel READ richTextLabel NOTIFY richTextLabelChanged FINAL)
|
||||
Q_PROPERTY(QKeySequence sequence READ sequence NOTIFY sequenceChanged FINAL)
|
||||
|
||||
QML_NAMED_ELEMENT(Mnemonic)
|
||||
QML_UNCREATABLE("Mnemonic is only available via attached properties")
|
||||
QML_ATTACHED(MnemonicAttached)
|
||||
public:
|
||||
explicit MnemonicAttached(QObject *parent = nullptr);
|
||||
|
||||
static MnemonicAttached *qmlAttachedProperties(QObject *object);
|
||||
|
||||
QString label() const;
|
||||
void setLabel(const QString &label);
|
||||
|
||||
QString richTextLabel() const;
|
||||
|
||||
QKeySequence sequence() const;
|
||||
|
||||
signals:
|
||||
void enabledChanged();
|
||||
void labelChanged();
|
||||
void richTextLabelChanged();
|
||||
void sequenceChanged();
|
||||
|
||||
private slots:
|
||||
void onAltPressed();
|
||||
void onAltReleased();
|
||||
|
||||
private:
|
||||
void updateRichText();
|
||||
|
||||
QString m_label;
|
||||
QString m_richTextLabel;
|
||||
bool m_active;
|
||||
};
|
||||
|
||||
#endif // MNEMONICATTACHED_H
|
Loading…
Reference in New Issue