Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qkeysequenceedit.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2013 Ivan Komissarov.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
8
9#include "qboxlayout.h"
10#include "qlineedit.h"
11#include <private/qkeymapper_p.h>
12
13using namespace std::chrono_literals;
14
15QT_BEGIN_NAMESPACE
16
17static_assert(QKeySequencePrivate::MaxKeyCount == 4); // assumed by the code around here
18
20{
21 Q_Q(QKeySequenceEdit);
22
23 lineEdit = new QLineEdit(q);
24 lineEdit->setObjectName(QStringLiteral("qt_keysequenceedit_lineedit"));
25 lineEdit->setClearButtonEnabled(false);
26 q->connect(lineEdit, &QLineEdit::textChanged, q, [q](const QString& text) {
27 // Clear the shortcut if the user clicked on the clear icon
28 if (text.isEmpty())
29 q->clear();
30 });
31
32 keyNum = 0;
33 prevKey = -1;
34 finishingKeyCombinations = {Qt::Key_Tab, Qt::Key_Backtab};
35
36 QVBoxLayout *layout = new QVBoxLayout(q);
37 layout->setContentsMargins(0, 0, 0, 0);
38 layout->addWidget(lineEdit);
39
40 std::fill_n(key, QKeySequencePrivate::MaxKeyCount, QKeyCombination::fromCombined(0));
41
42 lineEdit->setFocusProxy(q);
43 lineEdit->installEventFilter(q);
45
46 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
47 q->setFocusPolicy(Qt::StrongFocus);
48 q->setAttribute(Qt::WA_MacShowFocusRect, true);
49 q->setAttribute(Qt::WA_InputMethodEnabled, false);
50}
51
52int QKeySequenceEditPrivate::translateModifiers(Qt::KeyboardModifiers state, const QString &text)
53{
54 Q_UNUSED(text);
55 int result = 0;
56 if (state & Qt::ControlModifier)
57 result |= Qt::CTRL;
58 if (state & Qt::MetaModifier)
59 result |= Qt::META;
60 if (state & Qt::AltModifier)
61 result |= Qt::ALT;
62 return result;
63}
64
66{
67 if (releaseTimer.isActive())
68 releaseTimer.stop();
69 prevKey = -1;
70 lineEdit->setText(keySequence.toString(QKeySequence::NativeText));
71 lineEdit->setPlaceholderText(QKeySequenceEdit::tr("Press shortcut"));
72}
73
75{
76 Q_Q(QKeySequenceEdit);
77
79 emit q->keySequenceChanged(keySequence);
80 emit q->editingFinished();
81}
82
83/*!
84 \class QKeySequenceEdit
85 \brief The QKeySequenceEdit widget allows to input a QKeySequence.
86
87 \inmodule QtWidgets
88
89 \since 5.2
90
91 This widget lets the user choose a QKeySequence, which is usually used as
92 a shortcut. The recording is initiated when the widget receives the focus
93 and ends one second after the user releases the last key.
94
95 \sa QKeySequenceEdit::keySequence
96*/
97
98/*!
99 Constructs a QKeySequenceEdit widget with the given \a parent.
100*/
101QKeySequenceEdit::QKeySequenceEdit(QWidget *parent)
102 : QKeySequenceEdit(*new QKeySequenceEditPrivate, parent, { })
103{
104}
105
106/*!
107 Constructs a QKeySequenceEdit widget with the given \a keySequence and \a parent.
108*/
109QKeySequenceEdit::QKeySequenceEdit(const QKeySequence &keySequence, QWidget *parent)
110 : QKeySequenceEdit(parent)
111{
112 setKeySequence(keySequence);
113}
114
115/*!
116 \internal
117*/
118QKeySequenceEdit::QKeySequenceEdit(QKeySequenceEditPrivate &dd, QWidget *parent, Qt::WindowFlags f) :
119 QWidget(dd, parent, f)
120{
121 Q_D(QKeySequenceEdit);
122 d->init();
123}
124
125/*!
126 Destroys the QKeySequenceEdit object.
127*/
128QKeySequenceEdit::~QKeySequenceEdit()
129{
130}
131
132/*!
133 \property QKeySequenceEdit::keySequence
134
135 \brief This property contains the currently chosen key sequence.
136
137 The shortcut can be changed by the user or via setter function.
138
139 \note If the QKeySequence is longer than the maximumSequenceLength
140 property, the key sequence is truncated.
141*/
142QKeySequence QKeySequenceEdit::keySequence() const
143{
144 Q_D(const QKeySequenceEdit);
145
146 return d->keySequence;
147}
148
149/*!
150 \property QKeySequenceEdit::clearButtonEnabled
151 \brief Whether the key sequence edit displays a clear button when it is not
152 empty.
153
154 If enabled, the key sequence edit displays a trailing \e clear button when
155 it contains some text, otherwise the line edit does not show a clear button
156 (the default).
157
158 \since 6.4
159*/
160void QKeySequenceEdit::setClearButtonEnabled(bool enable)
161{
162 Q_D(QKeySequenceEdit);
163
164 d->lineEdit->setClearButtonEnabled(enable);
165}
166
167bool QKeySequenceEdit::isClearButtonEnabled() const
168{
169 Q_D(const QKeySequenceEdit);
170
171 return d->lineEdit->isClearButtonEnabled();
172}
173
174/*!
175 \property QKeySequenceEdit::maximumSequenceLength
176 \brief The maximum sequence length.
177
178 The maximum number of key sequences a user can enter. The value needs to
179 be between 1 and 4, with 4 being the default.
180
181 \since 6.5
182*/
183qsizetype QKeySequenceEdit::maximumSequenceLength() const
184{
185 Q_D(const QKeySequenceEdit);
186 return d->maximumSequenceLength;
187}
188
189void QKeySequenceEdit::setMaximumSequenceLength(qsizetype count)
190{
191 Q_D(QKeySequenceEdit);
192
193 if (count < 1 || count > QKeySequencePrivate::MaxKeyCount) {
194 qWarning("QKeySequenceEdit: maximumSequenceLength %lld is out of range (1..%d)",
195 qlonglong(count), QKeySequencePrivate::MaxKeyCount);
196 return;
197 }
198 d->maximumSequenceLength = int(count);
199 if (d->keyNum > count) {
200 for (qsizetype i = d->keyNum; i < count; ++i)
201 d->key[i] = QKeyCombination::fromCombined(0);
202 d->keyNum = count;
203 d->rebuildKeySequence();
204 }
205}
206
207/*!
208 \property QKeySequenceEdit::finishingKeyCombinations
209 \brief The list of key combinations that finish editing the key sequences.
210
211 Any combination in the list will finish the editing of key sequences.
212 All other key combinations can be recorded as part of a key sequence. By
213 default, Qt::Key_Tab and Qt::Key_Backtab will finish recording the key
214 sequence.
215
216 \since 6.5
217*/
218void QKeySequenceEdit::setFinishingKeyCombinations(const QList<QKeyCombination> &finishingKeyCombinations)
219{
220 Q_D(QKeySequenceEdit);
221
222 d->finishingKeyCombinations = finishingKeyCombinations;
223}
224
225QList<QKeyCombination> QKeySequenceEdit::finishingKeyCombinations() const
226{
227 Q_D(const QKeySequenceEdit);
228
229 return d->finishingKeyCombinations;
230}
231
232void QKeySequenceEdit::setKeySequence(const QKeySequence &keySequence)
233{
234 Q_D(QKeySequenceEdit);
235
236 d->resetState();
237
238 if (d->keySequence == keySequence)
239 return;
240
241 const auto desiredCount = keySequence.count();
242 if (desiredCount > d->maximumSequenceLength) {
243 qWarning("QKeySequenceEdit: setting a key sequence of length %d "
244 "when maximumSequenceLength is %d, truncating.",
245 desiredCount, d->maximumSequenceLength);
246 }
247
248 d->keyNum = std::min(desiredCount, d->maximumSequenceLength);
249 for (int i = 0; i < d->keyNum; ++i)
250 d->key[i] = keySequence[i];
251 for (int i = d->keyNum; i < QKeySequencePrivate::MaxKeyCount; ++i)
252 d->key[i] = QKeyCombination::fromCombined(0);
253
254 d->rebuildKeySequence();
255
256 d->lineEdit->setText(d->keySequence.toString(QKeySequence::NativeText));
257
258 emit keySequenceChanged(d->keySequence);
259}
260
261/*!
262 \fn void QKeySequenceEdit::editingFinished()
263
264 This signal is emitted when the user finishes entering the shortcut.
265
266 \note there is a one second delay before releasing the last key and
267 emitting this signal.
268*/
269
270/*!
271 \brief Clears the current key sequence.
272*/
273void QKeySequenceEdit::clear()
274{
275 setKeySequence(QKeySequence());
276}
277
278/*!
279 \reimp
280*/
281bool QKeySequenceEdit::event(QEvent *e)
282{
283 Q_D(const QKeySequenceEdit);
284
285 switch (e->type()) {
286 case QEvent::Shortcut:
287 return true;
288 case QEvent::ShortcutOverride:
289 e->accept();
290 return true;
291 case QEvent::KeyPress: {
292 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
293 if (!d->finishingKeyCombinations.contains(ke->keyCombination())) {
294 keyPressEvent(ke);
295 return true;
296 }
297 }
298 break;
299 default:
300 break;
301 }
302
303 return QWidget::event(e);
304}
305
306/*!
307 \reimp
308*/
309void QKeySequenceEdit::keyPressEvent(QKeyEvent *e)
310{
311 Q_D(QKeySequenceEdit);
312
313 if (d->finishingKeyCombinations.contains(e->keyCombination())) {
314 d->finishEditing();
315 return;
316 }
317
318 int nextKey = e->key();
319
320 if (d->prevKey == -1) {
321 clear();
322 d->prevKey = nextKey;
323 }
324
325 d->lineEdit->setPlaceholderText(QString());
326 if (nextKey == Qt::Key_Control
327 || nextKey == Qt::Key_Shift
328 || nextKey == Qt::Key_Meta
329 || nextKey == Qt::Key_Alt
330 || nextKey == Qt::Key_unknown) {
331 return;
332 }
333
334 QString selectedText = d->lineEdit->selectedText();
335 if (!selectedText.isEmpty() && selectedText == d->lineEdit->text()) {
336 clear();
337 if (nextKey == Qt::Key_Backspace)
338 return;
339 }
340
341 if (d->keyNum >= d->maximumSequenceLength)
342 return;
343
344 if (e->modifiers() & Qt::ShiftModifier) {
345 const QList<QKeyCombination> possibleKeys = QKeyMapper::possibleKeys(e);
346 int pkTotal = possibleKeys.size();
347 if (!pkTotal)
348 return;
349 bool found = false;
350 for (int i = 0; i < possibleKeys.size(); ++i) {
351 const int key = possibleKeys.at(i).toCombined();
352 if (key - nextKey == int(e->modifiers())
353 || (key == nextKey && e->modifiers() == Qt::ShiftModifier)) {
354 nextKey = key;
355 found = true;
356 break;
357 }
358 }
359 // Use as fallback
360 if (!found)
361 nextKey = possibleKeys.first().toCombined();
362 } else {
363 nextKey |= d->translateModifiers(e->modifiers(), e->text());
364 }
365
366
367 d->key[d->keyNum] = QKeyCombination::fromCombined(nextKey);
368 d->keyNum++;
369
370 d->rebuildKeySequence();
371 QString text = d->keySequence.toString(QKeySequence::NativeText);
372 if (d->keyNum < d->maximumSequenceLength) {
373 //: This text is an "unfinished" shortcut, expands like "Ctrl+A, ..."
374 text = tr("%1, ...").arg(text);
375 }
376 d->lineEdit->setText(text);
377 e->accept();
378}
379
380/*!
381 \reimp
382*/
383void QKeySequenceEdit::keyReleaseEvent(QKeyEvent *e)
384{
385 Q_D(QKeySequenceEdit);
386
387 if (d->prevKey == e->key()) {
388 if (d->keyNum < d->maximumSequenceLength)
389 d->releaseTimer.start(1s, this);
390 else
391 d->finishEditing();
392 }
393 e->accept();
394}
395
396/*!
397 \reimp
398*/
399void QKeySequenceEdit::timerEvent(QTimerEvent *e)
400{
401 Q_D(QKeySequenceEdit);
402 if (e->id() == d->releaseTimer.id()) {
403 d->finishEditing();
404 return;
405 }
406
407 QWidget::timerEvent(e);
408}
409
410/*!
411 \reimp
412*/
413void QKeySequenceEdit::focusOutEvent(QFocusEvent *e)
414{
415 Q_D(QKeySequenceEdit);
416 if (e->reason() != Qt::PopupFocusReason)
417 d->finishEditing();
418 QWidget::focusOutEvent(e);
419}
420
421QT_END_NAMESPACE
422
423#include "moc_qkeysequenceedit.cpp"
int translateModifiers(Qt::KeyboardModifiers state, const QString &text)