From af6f7179e8faf890d7845a68560732f46275e105 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 17 Jun 2016 11:36:09 -0700 Subject: tests/manual: add highdpi subdir to the test list Change-Id: Ib57b52598e2f452985e9fffd1458f2bf00ddc84f Reviewed-by: Oswald Buddenhagen Reviewed-by: Shawn Rutledge --- tests/manual/manual.pro | 1 + 1 file changed, 1 insertion(+) (limited to 'tests/manual') diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 8e77a321dd..801297790e 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -4,6 +4,7 @@ SUBDIRS = bearerex \ filetest \ foreignwindows \ gestures \ +highdpi \ inputmethodhints \ keypadnavigation \ lance \ -- cgit v1.2.3 From 2ceccc6f7fd3e7496df26d16119e9b7693932658 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 30 Jun 2016 12:07:18 +0200 Subject: Add manual test embeddedintoforeignwindow Add a test for embedding Qt windows into foreign windows. Complements the existing "foreignwindows" test (which embeds foreign windows into Qt). The test has a simple UI based on QRasterWindow allowing for checking events and geometries. Task-number: QTBUG-41186 Change-Id: Ie62a3e250ca666e2fa5c2e3ef37ef0654829397c Reviewed-by: Laszlo Agocs --- .../embeddedintoforeignwindow.pro | 7 + .../embeddedintoforeignwindow/itemwindow.cpp | 72 ++++++ .../manual/embeddedintoforeignwindow/itemwindow.h | 126 +++++++++ tests/manual/embeddedintoforeignwindow/main.cpp | 287 +++++++++++++++++++++ tests/manual/manual.pro | 3 +- 5 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro create mode 100644 tests/manual/embeddedintoforeignwindow/itemwindow.cpp create mode 100644 tests/manual/embeddedintoforeignwindow/itemwindow.h create mode 100644 tests/manual/embeddedintoforeignwindow/main.cpp (limited to 'tests/manual') diff --git a/tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro b/tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro new file mode 100644 index 0000000000..93da4b8c91 --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +QT += gui-private +CONFIG += console c++11 +CONFIG -= app_bundle +SOURCES += main.cpp itemwindow.cpp +HEADERS += itemwindow.h +include(../diaglib/diaglib.pri) diff --git a/tests/manual/embeddedintoforeignwindow/itemwindow.cpp b/tests/manual/embeddedintoforeignwindow/itemwindow.cpp new file mode 100644 index 0000000000..32a2e6557c --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/itemwindow.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemwindow.h" + +#include +#include + +void TextItem::paint(QPainter &painter) +{ + painter.fillRect(m_rect, m_col); + painter.drawRect(m_rect); + QTextOption textOption; + textOption.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + painter.drawText(m_rect, m_text, textOption); +} + +void ButtonItem::mouseEvent(QMouseEvent *mouseEvent) +{ + if (mouseEvent->type() == QEvent::MouseButtonPress && rect().contains(mouseEvent->pos())) { + mouseEvent->accept(); + emit clicked(); + } +} + +void ButtonItem::keyEvent(QKeyEvent *keyEvent) +{ + if (m_shortcut && keyEvent->type() == QEvent::KeyPress + && (keyEvent->key() + int(keyEvent->modifiers())) == m_shortcut) { + keyEvent->accept(); + emit clicked(); + } +} + +void ItemWindow::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QRect rect(QPoint(0, 0), size()); + painter.fillRect(rect, m_background); + foreach (Item *i, m_items) + i->paint(painter); +} diff --git a/tests/manual/embeddedintoforeignwindow/itemwindow.h b/tests/manual/embeddedintoforeignwindow/itemwindow.h new file mode 100644 index 0000000000..8ea9adaa16 --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/itemwindow.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ITEMWINDOW_H +#define ITEMWINDOW_H + +#include +#include + +QT_USE_NAMESPACE + +// ItemWindow: Primitive UI item classes for use with a ItemWindow based +// on QRasterWindow without widgets allowing a simple UI without widgets. + +// Basic item to be used in a ItemWindow +class Item : public QObject { + Q_OBJECT +public: + explicit Item(QObject *parent = nullptr) : QObject(parent) {} + + virtual void paint(QPainter &painter) = 0; + virtual void mouseEvent(QMouseEvent *) {} + virtual void keyEvent(QKeyEvent *) {} +}; + +// Positionable text item +class TextItem : public Item { + Q_OBJECT +public: + explicit TextItem(const QString &text, const QRect &rect, const QColor &col, + QObject *parent = nullptr) + : Item(parent), m_text(text), m_rect(rect), m_col(col) {} + + void paint(QPainter &painter) override; + + QRect rect() const { return m_rect; } + +private: + const QString m_text; + const QRect m_rect; + const QColor m_col; +}; + +// "button" item +class ButtonItem : public TextItem { + Q_OBJECT +public: + explicit ButtonItem(const QString &text, const QRect &rect, const QColor &col, + QObject *parent = nullptr) + : TextItem(text, rect, col, parent), m_shortcut(0) {} + + void mouseEvent(QMouseEvent *mouseEvent) override; + void keyEvent(QKeyEvent *keyEvent) override; + + int shortcut() const { return m_shortcut; } + void setShortcut(int shortcut) { m_shortcut = shortcut; } + +signals: + void clicked(); + +private: + int m_shortcut; +}; + +#define PROPAGATE_EVENT(windowHandler, eventClass, itemHandler) \ +void windowHandler(eventClass *e) override \ +{ \ + foreach (Item *i, m_items) \ + i->itemHandler(e); \ +} + +class ItemWindow : public QRasterWindow { + Q_OBJECT +public: + explicit ItemWindow(QWindow *parent = nullptr) : QRasterWindow(parent), m_background(Qt::white) {} + + void addItem(Item *item) { m_items.append(item); } + + QColor background() const { return m_background; } + void setBackground(const QColor &background) { m_background = background; } + +protected: + void paintEvent(QPaintEvent *) override; + PROPAGATE_EVENT(mousePressEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(mouseReleaseEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(mouseDoubleClickEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(mouseMoveEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(keyPressEvent, QKeyEvent, keyEvent) + PROPAGATE_EVENT(keyReleaseEvent, QKeyEvent, keyEvent) + +private: + QList m_items; + QColor m_background; +}; + +#endif // ITEMWINDOW_H diff --git a/tests/manual/embeddedintoforeignwindow/main.cpp b/tests/manual/embeddedintoforeignwindow/main.cpp new file mode 100644 index 0000000000..d7b33683a7 --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/main.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemwindow.h" + +#include + +#include +#include +#include +#include + +#ifdef Q_OS_WIN +# include +# include +# include +#endif + +#include // diaglib +#include + +#include + +QT_USE_NAMESPACE + +static const char usage[] = + "\nEmbeds a QWindow into a native foreign window passed on the command line.\n" + "When no window ID is passed, a test window is created (Windows only)."; + +static QString windowTitle() +{ + return QLatin1String(QT_VERSION_STR) + QLatin1Char(' ') + QGuiApplication::platformName(); +} + +#ifdef Q_OS_WIN +// Helper to create a native test window (Windows) +static QString registerWindowClass(const QString &name) +{ + QString result; + void *proc = DefWindowProc; + QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface(); + if (!QMetaObject::invokeMethod(ni, "registerWindowClass", Qt::DirectConnection, + Q_RETURN_ARG(QString, result), + Q_ARG(QString, name), + Q_ARG(void *, proc))) { + qWarning("registerWindowClass failed"); + } + return result; +} + +static HWND createNativeWindow(const QString &name) +{ + const HWND hwnd = + CreateWindowEx(0, reinterpret_cast(name.utf16()), + L"NativeWindow", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, GetModuleHandle(NULL), NULL); + + if (!hwnd) { + qErrnoWarning("Cannot create window \"%s\"", qPrintable(name)); + return 0; + } + + const QString text = windowTitle() + QLatin1String(" 0x") + QString::number(quint64(hwnd), 16); + SetWindowText(hwnd, reinterpret_cast(text.utf16())); + return hwnd; +} +#endif // Q_OS_WIN + +// Helper functions for simple management of native windows. +static WId createNativeTestWindow() +{ + WId result = 0; +#ifdef Q_OS_WIN + const QString className = registerWindowClass(QLatin1String("TestClass") + windowTitle()); + const HWND nativeWin = createNativeWindow(className); + result = WId(nativeWin); +#else // Q_OS_WIN + Q_UNIMPLEMENTED(); +#endif + return result; +} + +static void showNativeWindow(WId wid) +{ +#ifdef Q_OS_WIN + ShowWindow(HWND(wid), SW_SHOW); +#else // Q_OS_WIN + Q_UNUSED(wid) + Q_UNIMPLEMENTED(); +#endif +} + +static void setFocusToNativeWindow(WId wid) +{ +#ifdef Q_OS_WIN + SetFocus(HWND(wid)); +#else // Q_OS_WIN + Q_UNUSED(wid) + Q_UNIMPLEMENTED(); +#endif +} + +static void destroyNativeWindow(WId wid) +{ +#ifdef Q_OS_WIN + DestroyWindow(HWND(wid)); +#else // Q_OS_WIN + Q_UNUSED(wid) + Q_UNIMPLEMENTED(); +#endif +} + +// Main test window to be embedded into foreign window with some buttons. +class EmbeddedTestWindow : public ItemWindow { + Q_OBJECT +public: + explicit EmbeddedTestWindow(QWindow *parent = nullptr) : ItemWindow(parent) + { + const int spacing = 10; + const QSize buttonSize(100, 30); + const int width = 3 * buttonSize.width() + 4 * spacing; + + QPoint pos(spacing, spacing); + addItem(new TextItem(::windowTitle(), QRect(pos, QSize(width - 2 * spacing, buttonSize.height())), + Qt::white)); + + pos.ry() += 2 * spacing + buttonSize.height(); + + ButtonItem *mgi = new ButtonItem("Map to global", QRect(pos, buttonSize), + QColor(Qt::yellow).lighter(), this); + connect(mgi, &ButtonItem::clicked, this, &EmbeddedTestWindow::testMapToGlobal); + addItem(mgi); + + pos.rx() += buttonSize.width() + spacing; + ButtonItem *di = new ButtonItem("Dump Wins", QRect(pos, buttonSize), + QColor(Qt::cyan).lighter(), this); + connect(di, &ButtonItem::clicked, this, [] () { QtDiag::dumpAllWindows(); }); + addItem(di); + + pos.rx() += buttonSize.width() + spacing; + ButtonItem *qi = new ButtonItem("Quit", QRect(pos, buttonSize), + QColor(Qt::red).lighter(), this); + qi->setShortcut(Qt::CTRL + Qt::Key_Q); + connect(qi, &ButtonItem::clicked, qApp, &QCoreApplication::quit); + addItem(qi); + + setBackground(Qt::lightGray); + resize(width, pos.y() + buttonSize.height() + spacing); + } + +public slots: + void testMapToGlobal(); +}; + +void EmbeddedTestWindow::testMapToGlobal() +{ + const QPoint globalPos = mapToGlobal(QPoint(0,0)); + qDebug() << "mapToGlobal(QPoint(0,0)" << globalPos + << "cursor at:" << QCursor::pos(); +} + +struct EventFilterOption +{ + const char *name; + const char *description; + QtDiag::EventFilter::EventCategories categories; +}; + +EventFilterOption eventFilterOptions[] = { +{"mouse-events", "Dump mouse events.", QtDiag::EventFilter::MouseEvents}, +{"keyboard-events", "Dump keyboard events.", QtDiag::EventFilter::KeyEvents}, +{"state-events", "Dump state/focus change events.", QtDiag::EventFilter::StateChangeEvents | QtDiag::EventFilter::FocusEvents} +}; + +static inline bool isOptionSet(int argc, char *argv[], const char *option) +{ + return (argv + argc) != + std::find_if(argv + 1, argv + argc, + [option] (const char *arg) { return !qstrcmp(arg, option); }); +} + +int main(int argc, char *argv[]) +{ + // Check for no scaling before QApplication is instantiated. + if (isOptionSet(argc, argv, "-s")) + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); + QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); + QGuiApplication::setApplicationDisplayName("Foreign Window Embedding Tester"); + + QGuiApplication app(argc, argv); + + // Process command line + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + parser.setApplicationDescription(QLatin1String(usage)); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption noScalingDummy(QStringLiteral("s"), + QStringLiteral("Disable High DPI scaling.")); + parser.addOption(noScalingDummy); + const int eventFilterOptionCount = int(sizeof(eventFilterOptions) / sizeof(eventFilterOptions[0])); + for (int i = 0; i < eventFilterOptionCount; ++i) { + parser.addOption(QCommandLineOption(QLatin1String(eventFilterOptions[i].name), + QLatin1String(eventFilterOptions[i].description))); + } + parser.addPositionalArgument(QStringLiteral("[windows]"), QStringLiteral("Window ID.")); + + parser.process(QCoreApplication::arguments()); + + QtDiag::EventFilter::EventCategories eventCategories = 0; + for (int i = 0; i < eventFilterOptionCount; ++i) { + if (parser.isSet(QLatin1String(eventFilterOptions[i].name))) + eventCategories |= eventFilterOptions[i].categories; + } + if (eventCategories) + app.installEventFilter(new QtDiag::EventFilter(eventCategories, &app)); + + // Obtain foreign window to test with. + WId testForeignWinId = 0; + bool createdTestWindow = false; + if (parser.positionalArguments().isEmpty()) { + testForeignWinId = createNativeTestWindow(); + if (!testForeignWinId) + parser.showHelp(-1); + showNativeWindow(testForeignWinId); + createdTestWindow = true; + } else { + bool ok; + const QString &winIdArgument = parser.positionalArguments().constFirst(); + testForeignWinId = winIdArgument.toULongLong(&ok, 0); + if (!ok) { + std::cerr << "Invalid window id: \"" << qPrintable(winIdArgument) << "\"\n"; + return -1; + } + } + if (!testForeignWinId) + parser.showHelp(1); + + QWindow *foreignWindow = QWindow::fromWinId(testForeignWinId); + if (!foreignWindow) + return -2; + foreignWindow->setObjectName("ForeignWindow"); + EmbeddedTestWindow *embeddedTestWindow = new EmbeddedTestWindow(foreignWindow); + embeddedTestWindow->setObjectName("EmbeddedTestWindow"); + embeddedTestWindow->show(); + setFocusToNativeWindow(embeddedTestWindow->winId()); // Windows: Set keyboard focus. + + const int exitCode = app.exec(); + delete embeddedTestWindow; + delete foreignWindow; + if (createdTestWindow) + destroyNativeWindow(testForeignWinId); + return exitCode; +} + +#include "main.moc" diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 8e77a321dd..34fd6662ec 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -2,6 +2,7 @@ TEMPLATE=subdirs SUBDIRS = bearerex \ filetest \ +embeddedintoforeignwindow \ foreignwindows \ gestures \ inputmethodhints \ @@ -66,4 +67,4 @@ win32 { lessThan(QT_MAJOR_VERSION, 5): SUBDIRS -= bearerex lance qnetworkaccessmanager/qget qmimedatabase qnetworkreply \ qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency \ -foreignwindows +embeddedintoforeignwindow foreignwindows -- cgit v1.2.3