1/*
2 * SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#ifndef MNEMONICATTACHED_H
8#define MNEMONICATTACHED_H
9
10#include <QObject>
11#include <QQuickWindow>
12
13#include <QQmlEngine>
14
15/*!
16 * \qmltype MnemonicData
17 * \inqmlmodule org.kde.kirigami
18 *
19 * \brief An attached property used to calculate automated keyboard sequences
20 * to trigger actions based upon their text.
21 *
22 * If an "&" mnemonic is used (such as "&Ok"), the system will attempt to assign
23 * the desired letter giving it priority, otherwise a letter among the ones
24 * in the label will be used if possible and not conflicting.
25 *
26 * Different kinds of controls will have different priorities in assigning the
27 * shortcut: for instance the "Ok/Cancel" buttons in a dialog will have priority
28 * over fields of a FormLayout.
29 *
30 * Usually the developer shouldn't use this directly as base components
31 * already use this, but only when implementing a custom graphical Control.
32 * \since 2.3
33 */
34class MnemonicAttached : public QObject
35{
36 Q_OBJECT
37 QML_NAMED_ELEMENT(MnemonicData)
38 QML_ATTACHED(MnemonicAttached)
39 QML_UNCREATABLE("Cannot create objects of type MnemonicData, use it as an attached property")
40
41 /*!
42 * \qmlattachedproperty string MnemonicData::label
43 *
44 * The label of the control we want to compute a mnemonic for, instance
45 * "Label:" or "&Ok"
46 */
47 Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged FINAL)
48
49 /*!
50 * \qmlattachedproperty string MnemonicData::richTextLabel
51 *
52 * The user-visible final label, which will have the shortcut letter underlined,
53 * such as "<u>O</u>k".
54 */
55 Q_PROPERTY(QString richTextLabel READ richTextLabel NOTIFY richTextLabelChanged FINAL)
56
57 /*!
58 * \qmlattachedproperty string MnemonicData::mnemonicLabel
59 *
60 * The label with an "&" mnemonic in the place which will have the shortcut
61 * assigned, regardless of whether the & was assigned by the user or automatically generated.
62 */
63 Q_PROPERTY(QString mnemonicLabel READ mnemonicLabel NOTIFY mnemonicLabelChanged FINAL)
64
65 /*!
66 * \qmlattachedproperty string MnemonicData::plainTextLabel
67 *
68 * The label in plain text with no markup nor & markers.
69 */
70 Q_PROPERTY(QString plainTextLabel READ plainTextLabel NOTIFY plainTextLabelChanged FINAL)
71
72 /*!
73 * \qmlattachedproperty bool MnemonicData::enabled
74 *
75 * Only if true this mnemonic will be considered for the global assignment.
76 *
77 * default: true
78 */
79 Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
80
81 /*!
82 * \qmlattachedproperty enumeration MnemonicData::controlType
83 *
84 * The type of control this mnemonic is attached: different types of controls have different importance and priority for shortcut assignment.
85 *
86 * \qmlenumeratorsfrom MnemonicAttached::ControlType
87 */
88 Q_PROPERTY(MnemonicAttached::ControlType controlType READ controlType WRITE setControlType NOTIFY controlTypeChanged FINAL)
89
90 /*!
91 * \qmlattachedproperty keysequence MnemonicData::sequence
92 *
93 * The final key sequence assigned, if any: it will be Alt+alphanumeric char.
94 */
95 Q_PROPERTY(QKeySequence sequence READ sequence NOTIFY sequenceChanged FINAL)
96
97 /*!
98 * \qmlattachedproperty bool MnemonicData::active
99 *
100 * True when the user is pressing alt and the accelerators should be shown.
101 *
102 * \since 5.72
103 * \since 2.15
104 */
105 Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged FINAL)
106
107public:
108 /*!
109 * \value ActionElement pushbuttons, checkboxes etc
110 * \value DialogButton buttons for dialogs
111 * \value MenuItem Menu items
112 * \value FormLabel Buddy label in a FormLayout
113 * \value SecondaryControl Other controls that are considered not much important and low priority for shortcuts
114 */
115 enum ControlType {
116 ActionElement,
117 DialogButton,
118 MenuItem,
119 FormLabel,
120 SecondaryControl,
121 };
122 Q_ENUM(ControlType)
123
124 explicit MnemonicAttached(QObject *parent = nullptr);
125 ~MnemonicAttached() override;
126
127 void setLabel(const QString &text);
128 QString label() const;
129
130 QString richTextLabel() const;
131 QString mnemonicLabel() const;
132 QString plainTextLabel() const;
133
134 void setEnabled(bool enabled);
135 bool enabled() const;
136
137 void setControlType(MnemonicAttached::ControlType controlType);
138 ControlType controlType() const;
139
140 QKeySequence sequence();
141
142 void setActive(bool active);
143 bool active() const;
144
145 // QML attached property
146 static MnemonicAttached *qmlAttachedProperties(QObject *object);
147
148protected:
149 void updateSequence();
150
151Q_SIGNALS:
152 void labelChanged();
153 void enabledChanged();
154 void sequenceChanged();
155 void richTextLabelChanged();
156 void mnemonicLabelChanged();
157 void plainTextLabelChanged();
158 void controlTypeChanged();
159 void activeChanged();
160
161private:
162 QWindow *window() const;
163
164 void onAltPressed();
165 void onAltReleased();
166
167 void calculateWeights();
168
169 // TODO: to have support for DIALOG_BUTTON_EXTRA_WEIGHT etc, a type enum should be exported
170 enum {
171 // Additional weight for first character in string
172 FIRST_CHARACTER_EXTRA_WEIGHT = 50,
173 // Additional weight for the beginning of a word
174 WORD_BEGINNING_EXTRA_WEIGHT = 50,
175 // Additional weight for a 'wanted' accelerator ie string with '&'
176 WANTED_ACCEL_EXTRA_WEIGHT = 150,
177 // Default weight for an 'action' widget (ie, pushbuttons)
178 ACTION_ELEMENT_WEIGHT = 50,
179 // Additional weight for the dialog buttons (large, we basically never want these reassigned)
180 DIALOG_BUTTON_EXTRA_WEIGHT = 300,
181 // Weight for FormLayout labels (low)
182 FORM_LABEL_WEIGHT = 20,
183 // Weight for Secondary controls which are considered less important (low)
184 SECONDARY_CONTROL_WEIGHT = 10,
185 // Default weight for menu items
186 MENU_ITEM_WEIGHT = 250,
187 };
188
189 // order word letters by weight
190 int m_weight = 0;
191 int m_baseWeight = 0;
192 ControlType m_controlType = SecondaryControl;
193 QMap<int, QChar> m_weights;
194
195 QString m_label;
196 QString m_actualRichTextLabel;
197 QString m_richTextLabel;
198 QString m_mnemonicLabel;
199 QKeySequence m_sequence;
200 bool m_enabled = true;
201 bool m_active = false;
202
203 QPointer<QQuickWindow> m_window;
204
205 // global mapping of mnemonics
206 // TODO: map by QWindow
207 static QHash<QKeySequence, MnemonicAttached *> s_sequenceToObject;
208};
209
210QML_DECLARE_TYPEINFO(MnemonicAttached, QML_HAS_ATTACHED_PROPERTIES)
211
212#endif // MnemonicATTACHED_H
213

source code of kirigami/src/mnemonicattached.h