Add key events manual tests
Task-number: QTBUG-116873 Change-Id: I70c0199fa6244addf0f282aa8856935e3486270a Reviewed-by: Alexis Murzeau <amubtdx@gmail.com> Reviewed-by: Liang Qi <liang.qi@qt.io>
This commit is contained in:
parent
5743837a26
commit
ca68fa01fe
@ -19,6 +19,7 @@ endif()
|
|||||||
add_subdirectory(highdpi)
|
add_subdirectory(highdpi)
|
||||||
add_subdirectory(inputmethodhints)
|
add_subdirectory(inputmethodhints)
|
||||||
add_subdirectory(keypadnavigation)
|
add_subdirectory(keypadnavigation)
|
||||||
|
add_subdirectory(keyevents)
|
||||||
#add_subdirectory(lance) # qgl.h missing
|
#add_subdirectory(lance) # qgl.h missing
|
||||||
add_subdirectory(qcursor)
|
add_subdirectory(qcursor)
|
||||||
add_subdirectory(qdesktopservices)
|
add_subdirectory(qdesktopservices)
|
||||||
|
12
tests/manual/keyevents/CMakeLists.txt
Normal file
12
tests/manual/keyevents/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
qt_internal_add_manual_test(keyevents
|
||||||
|
GUI
|
||||||
|
SOURCES
|
||||||
|
main.cpp
|
||||||
|
LIBRARIES
|
||||||
|
Qt::Gui
|
||||||
|
Qt::GuiPrivate
|
||||||
|
Qt::Widgets
|
||||||
|
)
|
6
tests/manual/keyevents/keyevents.pro
Normal file
6
tests/manual/keyevents/keyevents.pro
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
QT += core gui widgets gui-private
|
||||||
|
|
||||||
|
TARGET = keyevents
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
304
tests/manual/keyevents/main.cpp
Normal file
304
tests/manual/keyevents/main.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <QtWidgets>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <private/qkeymapper_p.h>
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
|
||||||
|
static const QKeySequence keySequences[] = {
|
||||||
|
QKeySequence("Ctrl+C"),
|
||||||
|
QKeySequence("Ctrl++"),
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyEventModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit KeyEventModel(QObject *parent = nullptr)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Columns {
|
||||||
|
Language,
|
||||||
|
Direction,
|
||||||
|
Type,
|
||||||
|
ScanCode,
|
||||||
|
VirtualKey,
|
||||||
|
Modifiers,
|
||||||
|
Key,
|
||||||
|
Text,
|
||||||
|
PortableText,
|
||||||
|
NativeText,
|
||||||
|
PossibleKeys,
|
||||||
|
FirstKeySequence,
|
||||||
|
LastKeySequence = FirstKeySequence + std::size(keySequences) - 1,
|
||||||
|
KeySequenceEdit,
|
||||||
|
ColumnCount
|
||||||
|
};
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex & = QModelIndex()) const override
|
||||||
|
{
|
||||||
|
return int(m_events.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int columnCount(const QModelIndex & = QModelIndex()) const override
|
||||||
|
{
|
||||||
|
return ColumnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant headerData(int column, Qt::Orientation orientation, int role) const override
|
||||||
|
{
|
||||||
|
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
||||||
|
switch (column) {
|
||||||
|
case Language: return QString("language");
|
||||||
|
case Direction: return QString("direction");
|
||||||
|
case Type: return QString("type");
|
||||||
|
case ScanCode: return QString("nativeScanCode");
|
||||||
|
case VirtualKey: return QString("nativeVirtualKey");
|
||||||
|
case Modifiers: return QString("modifiers");
|
||||||
|
case Key: return QString("key");
|
||||||
|
case Text: return QString("text");
|
||||||
|
case PortableText: return QString("PortableText");
|
||||||
|
case NativeText: return QString("NativeText");
|
||||||
|
case PossibleKeys: return QString("keyCombinations");
|
||||||
|
case KeySequenceEdit: return m_customKeySequence.toString();
|
||||||
|
default: {
|
||||||
|
auto keySequence = keySequences[column - FirstKeySequence];
|
||||||
|
return keySequence.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static QString toString(T &&object, int verbosity = QDebug::DefaultVerbosity)
|
||||||
|
{
|
||||||
|
QString buffer;
|
||||||
|
QDebug stream(&buffer);
|
||||||
|
stream.setVerbosity(verbosity);
|
||||||
|
stream.nospace() << std::forward<T>(object);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
auto &event = m_events.at(index.row());
|
||||||
|
auto *keyEvent = event.keyEvent.get();
|
||||||
|
switch (int column = index.column()) {
|
||||||
|
case Language: return event.language;
|
||||||
|
case Direction: return toString(event.layoutDirection, QDebug::MinimumVerbosity);
|
||||||
|
case Type: return toString(keyEvent->type(), QDebug::MinimumVerbosity);
|
||||||
|
case ScanCode: return keyEvent->nativeScanCode();
|
||||||
|
case VirtualKey: return keyEvent->nativeVirtualKey();
|
||||||
|
case Modifiers: return toString(keyEvent->modifiers(), QDebug::MinimumVerbosity);
|
||||||
|
case Key: return toString(Qt::Key(keyEvent->key()), QDebug::MinimumVerbosity);
|
||||||
|
case Text: return keyEvent->text();
|
||||||
|
case PortableText: return event.keySequence.toString(QKeySequence::PortableText);
|
||||||
|
case NativeText: return event.keySequence.toString(QKeySequence::NativeText);
|
||||||
|
case PossibleKeys: {
|
||||||
|
QStringList keyCombinations;
|
||||||
|
for (auto combination : event.possibleKeyCombinations)
|
||||||
|
keyCombinations << QKeySequence(combination).toString(QKeySequence::NativeText);
|
||||||
|
return keyCombinations.join(" ");
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
QStringList matches;
|
||||||
|
if (event.keySequenceEquals[column - FirstKeySequence])
|
||||||
|
matches << "K";
|
||||||
|
if (event.shortcutMatches[column - FirstKeySequence])
|
||||||
|
matches << "S";
|
||||||
|
return matches.join(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (role == Qt::TextAlignmentRole) {
|
||||||
|
return Qt::AlignCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
std::unique_ptr<QKeyEvent> keyEvent;
|
||||||
|
QString language;
|
||||||
|
Qt::LayoutDirection layoutDirection;
|
||||||
|
QKeySequence keySequence;
|
||||||
|
using PossibleKeysList = decltype(std::declval<QKeyMapper>().possibleKeys(nullptr));
|
||||||
|
PossibleKeysList possibleKeyCombinations;
|
||||||
|
// Hard-coded key sequences, plus room for KeySequenceEdit
|
||||||
|
bool keySequenceEquals[std::size(keySequences) + 1] = {};
|
||||||
|
bool shortcutMatches[std::size(keySequences) + 1] = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool eventFilter(QObject *object, QEvent *event) override
|
||||||
|
{
|
||||||
|
if (!m_enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (auto type = event->type()) {
|
||||||
|
case QEvent::KeyPress:
|
||||||
|
case QEvent::KeyRelease: {
|
||||||
|
auto *keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
|
auto *inputMethod = qGuiApp->inputMethod();
|
||||||
|
auto row = int(m_events.size());
|
||||||
|
beginInsertRows(QModelIndex(), row, row);
|
||||||
|
m_events.push_back({
|
||||||
|
std::unique_ptr<QKeyEvent>(keyEvent->clone()),
|
||||||
|
QLocale::languageToString(inputMethod->locale().language()),
|
||||||
|
inputMethod->inputDirection(),
|
||||||
|
QKeySequence(keyEvent->keyCombination()),
|
||||||
|
QKeyMapper::instance()->possibleKeys(keyEvent)
|
||||||
|
});
|
||||||
|
|
||||||
|
Event &event = m_events.back();
|
||||||
|
|
||||||
|
if (type == QEvent::KeyPress) {
|
||||||
|
for (size_t i = 0; i < std::size(event.keySequenceEquals); ++i) {
|
||||||
|
QKeySequence keySequence = i == std::size(keySequences) ?
|
||||||
|
m_customKeySequence : keySequences[i];
|
||||||
|
|
||||||
|
event.keySequenceEquals[i] = event.keySequence == keySequence;
|
||||||
|
|
||||||
|
QShortcut shortcut(keySequence, object, [&] {
|
||||||
|
event.shortcutMatches[i] = true;
|
||||||
|
}, Qt::ApplicationShortcut);
|
||||||
|
QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap;
|
||||||
|
shortcutMap.tryShortcut(keyEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case QEvent::ShortcutOverride: {
|
||||||
|
auto *keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
|
if (!keyEvent->matches(QKeySequence::Quit)) {
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_events.clear();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void setCustomKeySequence(const QKeySequence &keySequence)
|
||||||
|
{
|
||||||
|
m_customKeySequence = keySequence;
|
||||||
|
emit headerDataChanged(Qt::Horizontal, KeySequenceEdit, ColumnCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_enabled = true;
|
||||||
|
std::vector<Event> m_events;
|
||||||
|
QKeySequence m_customKeySequence;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyEventWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeyEventWindow()
|
||||||
|
{
|
||||||
|
setWindowTitle(QString("Qt %1 on %2").arg(QT_VERSION_STR).arg(QSysInfo::prettyProductName()));
|
||||||
|
auto *tableView = new QTableView(this);
|
||||||
|
m_keyEventModel = new KeyEventModel(this);
|
||||||
|
tableView->setModel(m_keyEventModel);
|
||||||
|
tableView->installEventFilter(m_keyEventModel);
|
||||||
|
|
||||||
|
tableView->setFocusPolicy(Qt::ClickFocus);
|
||||||
|
tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
tableView->setSelectionMode(QAbstractItemView::NoSelection);
|
||||||
|
tableView->setWordWrap(false);
|
||||||
|
|
||||||
|
QObject::connect(tableView->model(), &QAbstractItemModel::rowsInserted,
|
||||||
|
tableView, &QTableView::scrollToBottom);
|
||||||
|
|
||||||
|
QMenu *menu = menuBar()->addMenu("File");
|
||||||
|
menu->addAction("Save...", this, &KeyEventWindow::save);
|
||||||
|
menu->addAction("Clear", this, [this]{
|
||||||
|
m_keyEventModel->reset();
|
||||||
|
});
|
||||||
|
auto *enableAction = menu->addAction("Enabled", this, [this]{
|
||||||
|
auto *action = static_cast<QAction*>(sender());
|
||||||
|
m_keyEventModel->m_enabled = action->isChecked();
|
||||||
|
});
|
||||||
|
enableAction->setCheckable(true);
|
||||||
|
enableAction->setChecked(true);
|
||||||
|
|
||||||
|
auto *toolBar = addToolBar("Tools");
|
||||||
|
toolBar->setMovable(false);
|
||||||
|
toolBar->addWidget(new QLabel("Key sequence editor:"));
|
||||||
|
auto *keySequenceEdit = new QKeySequenceEdit;
|
||||||
|
keySequenceEdit->setMaximumSequenceLength(1);
|
||||||
|
connect(keySequenceEdit, &QKeySequenceEdit::keySequenceChanged,
|
||||||
|
m_keyEventModel, &KeyEventModel::setCustomKeySequence);
|
||||||
|
keySequenceEdit->installEventFilter(m_keyEventModel);
|
||||||
|
toolBar->addWidget(keySequenceEdit);
|
||||||
|
toolBar->addWidget(new QLabel("Free form text input:"));
|
||||||
|
toolBar->addWidget(new QLineEdit);
|
||||||
|
|
||||||
|
setCentralWidget(tableView);
|
||||||
|
centralWidget()->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void save()
|
||||||
|
{
|
||||||
|
auto homeDirectory = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||||
|
QString fileName = QFileDialog::getSaveFileName(this, "Save events",
|
||||||
|
QString("%1/events.csv").arg(homeDirectory));
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||||
|
QMessageBox::critical(this, "Could not open file", file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QTextStream output(&file);
|
||||||
|
const auto columns = m_keyEventModel->columnCount();
|
||||||
|
for (int c = 0; c < columns; ++c) {
|
||||||
|
output << m_keyEventModel->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString()
|
||||||
|
<< ((c < columns - 1) ? ";" : "");
|
||||||
|
}
|
||||||
|
output << "\n";
|
||||||
|
for (int r = 0; r < m_keyEventModel->rowCount(); ++r) {
|
||||||
|
for (int c = 0; c < m_keyEventModel->columnCount(); ++c) {
|
||||||
|
auto index = m_keyEventModel->index(r, c);
|
||||||
|
output << m_keyEventModel->data(index).toString()
|
||||||
|
<< ((c < columns - 1) ? ";" : "");
|
||||||
|
}
|
||||||
|
output << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent *keyEvent) override
|
||||||
|
{
|
||||||
|
if (keyEvent->matches(QKeySequence::Quit))
|
||||||
|
qGuiApp->quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEventModel *m_keyEventModel = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
KeyEventWindow keyEventWindow;
|
||||||
|
keyEventWindow.showMaximized();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
@ -10,6 +10,7 @@ gestures \
|
|||||||
highdpi \
|
highdpi \
|
||||||
inputmethodhints \
|
inputmethodhints \
|
||||||
keypadnavigation \
|
keypadnavigation \
|
||||||
|
keyevents \
|
||||||
lance \
|
lance \
|
||||||
network_remote_stresstest \
|
network_remote_stresstest \
|
||||||
network_stresstest \
|
network_stresstest \
|
||||||
|
Loading…
Reference in New Issue
Block a user