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