diff options
Diffstat (limited to 'tests/manual')
28 files changed, 2182 insertions, 346 deletions
diff --git a/tests/manual/cocoa/qt_on_cocoa/main.mm b/tests/manual/cocoa/qt_on_cocoa/main.mm index 5dd546479e..23370b0305 100644 --- a/tests/manual/cocoa/qt_on_cocoa/main.mm +++ b/tests/manual/cocoa/qt_on_cocoa/main.mm @@ -31,123 +31,35 @@ ** ****************************************************************************/ -#include <QtGui> -#include <QtDeclarative> +#include "rasterwindow.h" +#include <QtGui> #include <QtWidgets/QtWidgets> -#include <private/qwidgetwindow_p.h> -#include <QtGui/qpa/qplatformnativeinterface.h> - -#include <QtGui/QPixmap> - -#include "window.h" #include <Cocoa/Cocoa.h> - -@interface FilledView : NSView -{ - +@interface AppDelegate : NSObject <NSApplicationDelegate> { + QGuiApplication *m_app; + QWindow *m_window; } +- (AppDelegate *) initWithArgc:(int)argc argv:(const char **)argv; +- (void) applicationWillFinishLaunching: (NSNotification *)notification; +- (void)applicationWillTerminate:(NSNotification *)notification; @end -@implementation FilledView - -- (void)drawRect:(NSRect)dirtyRect { - // set any NSColor for filling, say white: - [[NSColor redColor] setFill]; - NSRectFill(dirtyRect); -} - -@end - -@interface QtMacToolbarDelegate : NSObject <NSToolbarDelegate> +@implementation AppDelegate +- (AppDelegate *) initWithArgc:(int)argc argv:(const char **)argv { -@public - NSToolbar *toolbar; -} - -- (id)init; -- (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted; -- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)tb; -- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar; -- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar; -@end - -@implementation QtMacToolbarDelegate - -- (id)init -{ - self = [super init]; - if (self) { - } + m_app = new QGuiApplication(argc, const_cast<char **>(argv)); return self; } -- (void)dealloc -{ - [super dealloc]; -} - -- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)tb -{ - Q_UNUSED(tb); - NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease]; -// [array addObject : NSToolbarPrintItemIdentifier]; -// [array addObject : NSToolbarShowColorsItemIdentifier]; - [array addObject : @"filledView"]; - return array; -} - -- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)tb -{ - Q_UNUSED(tb); - NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease]; -// [array addObject : NSToolbarPrintItemIdentifier]; -// [array addObject : NSToolbarShowColorsItemIdentifier]; - [array addObject : @"filledView"]; - return array; -} - -- (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)tb -{ - Q_UNUSED(tb); - NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease]; - return array; -} - -- (IBAction)itemClicked:(id)sender +- (void) applicationWillFinishLaunching: (NSNotification *)notification { + Q_UNUSED(notification); -} - -- (NSToolbarItem *) toolbar: (NSToolbar *)tb itemForItemIdentifier: (NSString *) itemIdentifier willBeInsertedIntoToolbar:(BOOL) willBeInserted -{ - Q_UNUSED(tb); - Q_UNUSED(willBeInserted); - //const QString identifier = toQString(itemIdentifier); - //NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - //return toolbarItem; - - //NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - FilledView *theView = [[FilledView alloc] init]; - [toolbarItem setView : theView]; - [toolbarItem setMinSize : NSMakeSize(400, 40)]; - [toolbarItem setMaxSize : NSMakeSize(4000, 40)]; - return toolbarItem; -} -@end - -@interface WindowAndViewAndQtCreator : NSObject {} -- (void)createWindowAndViewAndQt; -@end - -@implementation WindowAndViewAndQtCreator -- (void)createWindowAndViewAndQt { - - // Create the window + // Create the NSWindow NSRect frame = NSMakeRect(500, 500, 500, 500); NSWindow* window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask @@ -156,49 +68,31 @@ NSString *title = @"This the NSWindow window"; [window setTitle:title]; - [window setBackgroundColor:[NSColor blueColor]]; - // Create a tool bar, set Qt delegate - NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier : @"foobartoolbar"]; - QtMacToolbarDelegate *delegate = [[QtMacToolbarDelegate alloc] init]; - [toolbar setDelegate : delegate]; - [window setToolbar : toolbar]; - - // Create the QWindow, don't show it. - Window *qtWindow = new Window(); - qtWindow->create(); - - //QSGView *qtWindow = new QSGView(); - //qtWindow->setSource(QUrl::fromLocalFile("/Users/msorvig/code/qt5/qtdeclarative/examples/declarative/samegame/samegame.qml")); - // qtWindow->setWindowFlags(Qt::WindowType(13)); // 13: NativeEmbeddedWindow - - // Get the nsview from the QWindow, set it as the content view - // on the NSWindow created above. - QPlatformNativeInterface *platformNativeInterface = QGuiApplication::platformNativeInterface(); - NSView *qtView = (NSView *)platformNativeInterface->nativeResourceForWindow("nsview", qtWindow); - [window setContentView:qtView]; + // Create the QWindow, use its NSView as the content view + m_window = new RasterWindow(); + [window setContentView:reinterpret_cast<NSView *>(m_window->winId())]; + + // Show the NSWindow [window makeKeyAndOrderFront:NSApp]; } -@end -int main(int argc, char *argv[]) +- (void)applicationWillTerminate:(NSNotification *)notification { - QGuiApplication app(argc, argv); - - // fake NSApplicationMain() implementation follows: - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; + Q_UNUSED(notification); + delete m_window; + delete m_app; +} - // schedule call to create the UI. - WindowAndViewAndQtCreator *windowAndViewAndQtCreator= [WindowAndViewAndQtCreator alloc]; - [NSTimer scheduledTimerWithTimeInterval:0 target:windowAndViewAndQtCreator selector:@selector(createWindowAndViewAndQt) userInfo:nil repeats:NO]; +@end - [(NSApplication *)NSApp run]; - [NSApp release]; - [pool release]; - exit(0); - return 0; +int main(int argc, const char *argv[]) +{ + // Create NSApplicaiton with delgate + NSApplication *app =[NSApplication sharedApplication]; + app.delegate = [[AppDelegate alloc] initWithArgc:argc argv:argv]; + return NSApplicationMain (argc, argv); } diff --git a/tests/manual/cocoa/qt_on_cocoa/qt_on_cocoa.pro b/tests/manual/cocoa/qt_on_cocoa/qt_on_cocoa.pro index 3d526909a5..97e4473e15 100644 --- a/tests/manual/cocoa/qt_on_cocoa/qt_on_cocoa.pro +++ b/tests/manual/cocoa/qt_on_cocoa/qt_on_cocoa.pro @@ -1,13 +1,11 @@ TEMPLATE = app OBJECTIVE_SOURCES += main.mm -HEADERS += window.h -SOURCES += window.cpp +HEADERS += rasterwindow.h +SOURCES += rasterwindow.cpp LIBS += -framework Cocoa -QMAKE_INFO_PLIST = Info_mac.plist -OTHER_FILES = Info_mac.plist -QT += gui widgets widgets-private gui-private core-private +QT += gui widgets quick -QT += declarative +QT += quick DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/manual/cocoa/qt_on_cocoa/window.cpp b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp index 9929a50065..9b8f5e63ce 100644 --- a/tests/manual/cocoa/qt_on_cocoa/window.cpp +++ b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp @@ -31,9 +31,9 @@ ** ****************************************************************************/ -#include "window.h" +#include "rasterwindow.h" -#include <private/qguiapplication_p.h> +//#include <private/qguiapplication_p.h> #include <QBackingStore> #include <QPainter> @@ -48,21 +48,14 @@ QColor colorTable[] = QColor("#c0ef8f") }; -Window::Window(QScreen *screen) - : QWindow(screen) +RasterWindow::RasterWindow(QRasterWindow *parent) + : QRasterWindow(parent) , m_backgroundColorIndex(colorIndexId++) { initialize(); } -Window::Window(QWindow *parent) - : QWindow(parent) - , m_backgroundColorIndex(colorIndexId++) -{ - initialize(); -} - -void Window::initialize() +void RasterWindow::initialize() { if (parent()) setGeometry(QRect(160, 120, 320, 240)); @@ -85,12 +78,12 @@ void Window::initialize() m_renderTimer = 0; } -void Window::mousePressEvent(QMouseEvent *event) +void RasterWindow::mousePressEvent(QMouseEvent *event) { m_lastPos = event->pos(); } -void Window::mouseMoveEvent(QMouseEvent *event) +void RasterWindow::mouseMoveEvent(QMouseEvent *event) { if (m_lastPos != QPoint(-1, -1)) { QPainter p(&m_image); @@ -102,7 +95,7 @@ void Window::mouseMoveEvent(QMouseEvent *event) scheduleRender(); } -void Window::mouseReleaseEvent(QMouseEvent *event) +void RasterWindow::mouseReleaseEvent(QMouseEvent *event) { if (m_lastPos != QPoint(-1, -1)) { QPainter p(&m_image); @@ -114,16 +107,16 @@ void Window::mouseReleaseEvent(QMouseEvent *event) scheduleRender(); } -void Window::exposeEvent(QExposeEvent *) +void RasterWindow::exposeEvent(QExposeEvent *) { scheduleRender(); } -void Window::resizeEvent(QResizeEvent *) +void RasterWindow::resizeEvent(QResizeEvent *) { QImage old = m_image; - //qDebug() << "Window::resizeEvent" << width << height; + //qDebug() << "RasterWindow::resizeEvent" << width << height; int width = qMax(geometry().width(), old.width()); int height = qMax(geometry().height(), old.height()); @@ -139,7 +132,7 @@ void Window::resizeEvent(QResizeEvent *) render(); } -void Window::keyPressEvent(QKeyEvent *event) +void RasterWindow::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Backspace: @@ -156,20 +149,20 @@ void Window::keyPressEvent(QKeyEvent *event) scheduleRender(); } -void Window::scheduleRender() +void RasterWindow::scheduleRender() { if (!m_renderTimer) m_renderTimer = startTimer(1); } -void Window::timerEvent(QTimerEvent *) +void RasterWindow::timerEvent(QTimerEvent *) { render(); killTimer(m_renderTimer); m_renderTimer = 0; } -void Window::render() +void RasterWindow::render() { QRect rect(QPoint(), geometry().size()); diff --git a/tests/manual/cocoa/qt_on_cocoa/window.h b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.h index a36180e0f3..1de66b5302 100644 --- a/tests/manual/cocoa/qt_on_cocoa/window.h +++ b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.h @@ -31,14 +31,13 @@ ** ****************************************************************************/ -#include <QWindow> +#include <QRasterWindow> #include <QImage> -class Window : public QWindow +class RasterWindow : public QRasterWindow { public: - Window(QWindow *parent = 0); - Window(QScreen *screen); + RasterWindow(QRasterWindow *parent = 0); protected: void mousePressEvent(QMouseEvent *); diff --git a/tests/manual/diaglib/eventfilter.cpp b/tests/manual/diaglib/eventfilter.cpp index b35d29cc8a..6df885ebb6 100644 --- a/tests/manual/diaglib/eventfilter.cpp +++ b/tests/manual/diaglib/eventfilter.cpp @@ -190,8 +190,13 @@ static void formatApplicationState(QDebug debug) debug << "\n QGuiApplication::modalWindow = "; formatObject(mw, debug); } - debug << "\n QGuiApplication::focusWindow = "; - formatObject(QGuiApplication::focusWindow(), debug); + const QObject *focusObject = QGuiApplication::focusObject(); + const QObject *focusWindow = QGuiApplication::focusWindow(); + debug << "\n QGuiApplication::focusObject = "; + formatObject(focusObject, debug); + if (focusWindow && focusWindow != focusObject) + debug << "\n QGuiApplication::focusWindow = "; + formatObject(focusWindow, debug); #endif // HAVE_GUI_APPLICATION } diff --git a/tests/manual/diaglib/textdump.cpp b/tests/manual/diaglib/textdump.cpp index ed4d5021be..0f69166a43 100644 --- a/tests/manual/diaglib/textdump.cpp +++ b/tests/manual/diaglib/textdump.cpp @@ -248,6 +248,15 @@ static const EnumLookup scriptEnumLookup[] = {QChar::Script_Tirhuta, "Script_Tirhuta"}, {QChar::Script_WarangCiti, "Script_WarangCiti"}, #endif // Qt 5.5 + +#if QT_VERSION >= 0x050600 + {QChar::Script_Ahom, "Script_Ahom"}, + {QChar::Script_AnatolianHieroglyphs, "Script_AnatolianHieroglyphs"}, + {QChar::Script_Hatran, "Script_Hatran"}, + {QChar::Script_Multani, "Script_Multani"}, + {QChar::Script_OldHungarian, "Script_OldHungarian"}, + {QChar::Script_SignWriting, "Script_SignWriting"}, +#endif // Qt 5.5 }; #endif // Qt 5.1 @@ -364,6 +373,9 @@ static const EnumLookup unicodeVersionEnumLookup[] = #if QT_VERSION >= 0x050500 {QChar::Unicode_7_0, "Unicode_7_0"}, #endif // Qt 5.5 +#if QT_VERSION >= 0x050600 + {QChar::Unicode_8_0, "Unicode_8_0"}, +#endif // Qt 5.6 #endif // Qt 5 }; diff --git a/tests/manual/highdpi/dragwidget.cpp b/tests/manual/highdpi/dragwidget.cpp new file mode 100644 index 0000000000..b203566696 --- /dev/null +++ b/tests/manual/highdpi/dragwidget.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 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 <QtWidgets> +#include "dragwidget.h" + +class FramedLabel : public QLabel +{ +public: + FramedLabel(const QString &text, QWidget *parent) + : QLabel(text, parent) + { + setAutoFillBackground(true); + setFrameShape(QFrame::Panel); + setFrameShadow(QFrame::Raised); + } +}; + +DragWidget::DragWidget(QString text, QWidget *parent) + : QWidget(parent), otherWindow(0) +{ + int x = 5; + int y = 5; + + bool createChildWindow = text.isEmpty(); // OK, yes this is a hack... + if (text.isEmpty()) + text = "You can drag from this window and drop text here"; + + QStringList words = text.split(' '); + foreach (QString word, words) { + if (!word.isEmpty()) { + FramedLabel *wordLabel = new FramedLabel(word, this); + wordLabel->move(x, y); + wordLabel->show(); + x += wordLabel->width() + 2; + if (x >= 245) { + x = 5; + y += wordLabel->height() + 2; + } + } + } + + /* + QPalette newPalette = palette(); + newPalette.setColor(QPalette::Window, Qt::white); + setPalette(newPalette); + */ + + setAcceptDrops(true); + setMinimumSize(400, qMax(200, y)); + setWindowTitle(tr("Draggable Text Window %1").arg(createChildWindow ? 1 : 2)); + if (createChildWindow) + otherWindow = new DragWidget("Here is a second window that accepts drops"); +} + +void DragWidget::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasText()) { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void DragWidget::dragMoveEvent(QDragMoveEvent * event) +{ + dragPos = event->pos(); + dragTimer.start(500, this); + update(); +} + +void DragWidget::dragLeaveEvent(QDragLeaveEvent *) +{ + dragTimer.stop(); + update(); +} + + +void DragWidget::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasText()) { + const QMimeData *mime = event->mimeData(); + QStringList pieces = mime->text().split(QRegExp("\\s+"), + QString::SkipEmptyParts); + QPoint position = event->pos(); + QPoint hotSpot; + + QList<QByteArray> hotSpotPos = mime->data("application/x-hotspot").split(' '); + if (hotSpotPos.size() == 2) { + hotSpot.setX(hotSpotPos.first().toInt()); + hotSpot.setY(hotSpotPos.last().toInt()); + } + dropPos = position - hotSpot; + dropTimer.start(500, this); + update(); + + foreach (QString piece, pieces) { + FramedLabel *newLabel = new FramedLabel(piece, this); + newLabel->move(position - hotSpot); + newLabel->show(); + + position += QPoint(newLabel->width(), 0); + } + + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } + foreach (QObject *child, children()) { + if (child->inherits("QWidget")) { + QWidget *widget = static_cast<QWidget *>(child); + if (!widget->isVisible()) + widget->deleteLater(); + } + } +} + +void DragWidget::mousePressEvent(QMouseEvent *event) +{ + QLabel *child = static_cast<QLabel*>(childAt(event->pos())); + if (!child) + return; + + QPoint hotSpot = event->pos() - child->pos(); + + QMimeData *mimeData = new QMimeData; + mimeData->setText(child->text()); + mimeData->setData("application/x-hotspot", + QByteArray::number(hotSpot.x()) + " " + QByteArray::number(hotSpot.y())); + + QPixmap pixmap(child->size()); + child->render(&pixmap); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(pixmap); + drag->setHotSpot(hotSpot); + + Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); + + if (dropAction == Qt::MoveAction) + child->close(); +} + +void DragWidget::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == dragTimer.timerId()) + dragTimer.stop(); + if (e->timerId() == dropTimer.timerId()) + dropTimer.stop(); + update(); +} + +void DragWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.fillRect(rect(), Qt::white); + + if (dropTimer.isActive()) { + p.setBrush(Qt::red); + p.drawEllipse(dropPos, 50, 50); + } + + if (dragTimer.isActive()) { + p.setPen(QPen(Qt::blue, 5)); + QPoint p1 = (rect().topLeft()*3 + rect().bottomRight())/4; + QPoint p2 = (rect().topLeft() + rect().bottomRight()*3)/4; + p.drawLine(p1, dragPos); + p.drawLine(p2, dragPos); + } +} + +void DragWidget::showEvent(QShowEvent *) +{ + if (otherWindow) + otherWindow->show(); +} + +void DragWidget::hideEvent(QHideEvent *) +{ + if (otherWindow) + otherWindow->hide(); +} diff --git a/tests/manual/highdpi/dragwidget.h b/tests/manual/highdpi/dragwidget.h new file mode 100644 index 0000000000..0d9631e2f8 --- /dev/null +++ b/tests/manual/highdpi/dragwidget.h @@ -0,0 +1,68 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 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 DRAGWIDGET_H +#define DRAGWIDGET_H + +#include <QWidget> +#include <QBasicTimer> + +QT_BEGIN_NAMESPACE +class QDragEnterEvent; +class QDropEvent; +QT_END_NAMESPACE + +class DragWidget : public QWidget +{ +public: + DragWidget(QString text = QString(), QWidget *parent = 0); + +protected: + void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; + void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void dragMoveEvent(QDragMoveEvent * event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE; + void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; + void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; +private: + QPoint dragPos; + QPoint dropPos; + QBasicTimer dragTimer; + QBasicTimer dropTimer; + QWidget *otherWindow; +}; + +#endif // DRAGWIDGET_H diff --git a/tests/manual/highdpi/highdpi.pro b/tests/manual/highdpi/highdpi.pro index 7a2979c74c..7d6b42535e 100644 --- a/tests/manual/highdpi/highdpi.pro +++ b/tests/manual/highdpi/highdpi.pro @@ -1,10 +1,17 @@ TEMPLATE = app TARGET = highdpi INCLUDEPATH += . -QT += widgets -CONFIG+=console +QT += widgets gui-private +CONFIG +=console +CONFIG -= app_bundle +CONFIG += c++11 # Input -SOURCES += main.cpp +SOURCES += \ + dragwidget.cpp \ + main.cpp + +HEADERS += \ + dragwidget.h RESOURCES += \ highdpi.qrc diff --git a/tests/manual/highdpi/highdpi.qrc b/tests/manual/highdpi/highdpi.qrc index 10efac44fa..0e33ed33d7 100644 --- a/tests/manual/highdpi/highdpi.qrc +++ b/tests/manual/highdpi/highdpi.qrc @@ -2,6 +2,7 @@ <qresource prefix="/"> <file>qticon16.png</file> <file>qticon16@2x.png</file> + <file>qticon16@3x.png</file> <file>qticon32.png</file> <file>qticon32@2x.png</file> <file>qticon64.png</file> diff --git a/tests/manual/highdpi/main.cpp b/tests/manual/highdpi/main.cpp index fd14523a97..692a60d511 100644 --- a/tests/manual/highdpi/main.cpp +++ b/tests/manual/highdpi/main.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include <QMainWindow> +#include <QMenuBar> #include <QLabel> #include <QHBoxLayout> #include <QApplication> @@ -39,6 +40,7 @@ #include <QStyle> #include <QToolBar> #include <QPushButton> +#include <QButtonGroup> #include <QLineEdit> #include <QScrollBar> #include <QSlider> @@ -49,10 +51,219 @@ #include <QWindow> #include <QScreen> #include <QFile> +#include <QMouseEvent> #include <QTemporaryDir> +#include <QTimer> #include <QCommandLineParser> #include <QCommandLineOption> +#include <QDebug> +#include <private/qhighdpiscaling_p.h> +#include "dragwidget.h" + +class DemoContainerBase +{ +public: + DemoContainerBase() : m_widget(0) {} + virtual ~DemoContainerBase() {} + QString name() { return option().names().first(); } + virtual QCommandLineOption &option() = 0; + virtual void makeVisible(bool visible, QWidget *parent) = 0; + QWidget *widget() { return m_widget; } +protected: + QWidget *m_widget; +}; + +typedef QList<DemoContainerBase*> DemoContainerList ; + + +template <class T> +class DemoContainer : public DemoContainerBase +{ +public: + DemoContainer(const QString &optionName, const QString &description) + : m_option(optionName, description) + { + } + ~DemoContainer() { delete m_widget; } + + QCommandLineOption &option() { return m_option; } + + void makeVisible(bool visible, QWidget *parent) { + if (visible && !m_widget) { + m_widget = new T; + m_widget->installEventFilter(parent); + } + if (m_widget) + m_widget->setVisible(visible); + } +private: + QCommandLineOption m_option; +}; + +class LabelSlider : public QObject +{ +Q_OBJECT +public: + LabelSlider(QObject *parent, const QString &text, QGridLayout *layout, int row) + : QObject(parent) + { + QLabel *textLabel = new QLabel(text); + m_slider = new QSlider(); + m_slider->setOrientation(Qt::Horizontal); + m_slider->setMinimum(1); + m_slider->setMaximum(40); + m_slider->setValue(10); + m_slider->setTracking(false); + m_slider->setTickInterval(5); + m_slider->setTickPosition(QSlider::TicksBelow); + m_label = new QLabel("1.0"); + + // set up layouts + layout->addWidget(textLabel, row, 0); + layout->addWidget(m_slider, row, 1); + layout->addWidget(m_label, row, 2); + + // handle slider position change + connect(m_slider, &QSlider::sliderMoved, this, &LabelSlider::updateLabel); + connect(m_slider, &QSlider::valueChanged, this, &LabelSlider::valueChanged); + } + void setValue(int scaleFactor) { + m_slider->setValue(scaleFactor); + updateLabel(scaleFactor); + } +private slots: + void updateLabel(int scaleFactor) { + // slider value is scale factor times ten; + qreal scalefactorF = qreal(scaleFactor) / 10.0; + + // update label, add ".0" if needed. + QString number = QString::number(scalefactorF); + if (!number.contains(".")) + number.append(".0"); + m_label->setText(number); + } +signals: + void valueChanged(int scaleFactor); +private: + QSlider *m_slider; + QLabel *m_label; +}; + +static qreal getScreenFactorWithoutPixelDensity(const QScreen *screen) +{ + // this is a hack that relies on knowing the internals of QHighDpiScaling + static const char *scaleFactorProperty = "_q_scaleFactor"; + QVariant screenFactor = screen->property(scaleFactorProperty); + return screenFactor.isValid() ? screenFactor.toReal() : 1.0; +} + +static inline qreal getGlobalScaleFactor() +{ + QScreen *noScreen = 0; + return QHighDpiScaling::factor(noScreen); +} + +class DemoController : public QWidget +{ +Q_OBJECT +public: + DemoController(DemoContainerList *demos, QCommandLineParser *parser); + ~DemoController(); +protected: + bool eventFilter(QObject *object, QEvent *event); + void closeEvent(QCloseEvent *) { qApp->quit(); } +private slots: + void handleButton(int id, bool toggled); +private: + DemoContainerList *m_demos; + QButtonGroup *m_group; +}; + +DemoController::DemoController(DemoContainerList *demos, QCommandLineParser *parser) + : m_demos(demos) +{ + setWindowTitle("screen scale factors"); + setObjectName("controller"); // make WindowScaleFactorSetter skip this window + + QGridLayout *layout = new QGridLayout; + setLayout(layout); + + int layoutRow = 0; + LabelSlider *globalScaleSlider = new LabelSlider(this, "Global scale factor", layout, layoutRow++); + globalScaleSlider->setValue(int(getGlobalScaleFactor() * 10)); + connect(globalScaleSlider, &LabelSlider::valueChanged, [](int scaleFactor){ + // slider value is scale factor times ten; + qreal scalefactorF = qreal(scaleFactor) / 10.0; + QHighDpiScaling::setGlobalFactor(scalefactorF); + }); + + // set up one scale control line per screen + QList<QScreen *> screens = QGuiApplication::screens(); + foreach (QScreen *screen, screens) { + // create scale control line + QSize screenSize = screen->geometry().size(); + QString screenId = screen->name() + " " + QString::number(screenSize.width()) + + " " + QString::number(screenSize.height()); + LabelSlider *slider = new LabelSlider(this, screenId, layout, layoutRow++); + slider->setValue(getScreenFactorWithoutPixelDensity(screen) * 10); + + // handle slider value change + connect(slider, &LabelSlider::valueChanged, [screen](int scaleFactor){ + // slider value is scale factor times ten; + qreal scalefactorF = qreal(scaleFactor) / 10.0; + + // set scale factor for screen + qreal oldFactor = QHighDpiScaling::factor(screen); + QHighDpiScaling::setScreenFactor(screen, scalefactorF); + qreal newFactor = QHighDpiScaling::factor(screen); + + qDebug() << "factor was / is" << oldFactor << newFactor; + }); + } + + m_group = new QButtonGroup(this); + m_group->setExclusive(false); + + for (int i = 0; i < m_demos->size(); ++i) { + DemoContainerBase *demo = m_demos->at(i); + QPushButton *button = new QPushButton(demo->name()); + button->setToolTip(demo->option().description()); + button->setCheckable(true); + layout->addWidget(button, layoutRow++, 0, 1, -1); + m_group->addButton(button, i); + + if (parser->isSet(demo->option())) { + demo->makeVisible(true, this); + button->setChecked(true); + } + } + connect(m_group, SIGNAL(buttonToggled(int, bool)), this, SLOT(handleButton(int, bool))); +} + +DemoController::~DemoController() +{ + qDeleteAll(*m_demos); +} + +bool DemoController::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::Close) { + for (int i = 0; i < m_demos->size(); ++i) { + DemoContainerBase *demo = m_demos->at(i); + if (demo->widget() == object) { + m_group->button(i)->setChecked(false); + break; + } + } + } + return false; +} + +void DemoController::handleButton(int id, bool toggled) +{ + m_demos->at(id)->makeVisible(toggled, this); +} class PixmapPainter : public QWidget { @@ -69,7 +280,6 @@ public: QIcon qtIcon; }; - PixmapPainter::PixmapPainter() { pixmap1X = QPixmap(":/qticon32.png"); @@ -172,15 +382,18 @@ class MainWindow : public QMainWindow { public: MainWindow(); + QMenu *addNewMenu(const QString &title, int itemCount = 5); QIcon qtIcon; QIcon qtIcon1x; QIcon qtIcon2x; QToolBar *fileToolBar; + int menuCount; }; MainWindow::MainWindow() + :menuCount(0) { // beware that QIcon auto-loads the @2x versions. qtIcon1x.addFile(":/qticon16.png"); @@ -192,8 +405,33 @@ MainWindow::MainWindow() // fileToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); fileToolBar->addAction(new QAction(qtIcon1x, QString("1x"), this)); fileToolBar->addAction(new QAction(qtIcon2x, QString("2x"), this)); + addNewMenu("&Edit"); + addNewMenu("&Build"); + addNewMenu("&Debug", 4); + addNewMenu("&Transmogrify", 7); + addNewMenu("T&ools"); + addNewMenu("&Help", 2); } + +QMenu *MainWindow::addNewMenu(const QString &title, int itemCount) +{ + QMenu *menu = menuBar()->addMenu(title); + for (int i = 0; i < itemCount; i++) { + menuCount++; + QString s = "Menu item " + QString::number(menuCount); + if (i == 3) { + QMenu *subMenu = menu->addMenu(s); + for (int j = 1; j < 4; j++) + subMenu->addAction(QString::fromLatin1("SubMenu item %1.%2").arg(menuCount).arg(j)); + } else { + menu->addAction(s); + } + } + return menu; +} + + class StandardIcons : public QWidget { public: @@ -205,7 +443,7 @@ public: int dy = 50; int maxX = 500; - for (int iconIndex = QStyle::SP_TitleBarMenuButton; iconIndex < QStyle::SP_MediaVolumeMuted; ++iconIndex) { + for (uint iconIndex = QStyle::SP_TitleBarMenuButton; iconIndex < QStyle::SP_MediaVolumeMuted; ++iconIndex) { QIcon icon = qApp->style()->standardIcon(QStyle::StandardPixmap(iconIndex)); QPainter p(this); p.drawPixmap(x, y, icon.pixmap(dx - 5, dy - 5)); @@ -295,14 +533,27 @@ public: void paintEvent(QPaintEvent *) { QPainter painter(this); - int y = 40; - for (int fontSize = 2; fontSize < 18; fontSize += 2) { + + // Points + int y = 10; + for (int fontSize = 6; fontSize < 18; fontSize += 2) { QFont font; font.setPointSize(fontSize); - QString string = QString(QStringLiteral("%1 The quick brown fox jumped over the lazy Doug.")).arg(fontSize); + QString string = QString(QStringLiteral("This text is in point size %1")).arg(fontSize); + painter.setFont(font); + y += (painter.fontMetrics().lineSpacing()); + painter.drawText(10, y, string); + } + + // Pixels + y += painter.fontMetrics().lineSpacing(); + for (int fontSize = 6; fontSize < 18; fontSize += 2) { + QFont font; + font.setPixelSize(fontSize); + QString string = QString(QStringLiteral("This text is in pixel size %1")).arg(fontSize); painter.setFont(font); + y += (painter.fontMetrics().lineSpacing()); painter.drawText(10, y, string); - y += (fontSize * 2.5); } } }; @@ -461,97 +712,427 @@ public: } }; +class LinePainter : public QWidget +{ +public: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + QPoint lastMousePoint; + QVector<QPoint> linePoints; +}; -int main(int argc, char **argv) +void LinePainter::paintEvent(QPaintEvent *) { - QApplication app(argc, argv); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), QBrush(Qt::gray)); - QCommandLineParser parser; - parser.setApplicationDescription("High DPI tester"); - parser.addHelpOption(); - parser.addVersionOption(); - QCommandLineOption pixmapPainterOption("pixmap", "Test pixmap painter"); - parser.addOption(pixmapPainterOption); - QCommandLineOption labelOption("label", "Test Labels"); - parser.addOption(labelOption); - QCommandLineOption mainWindowOption("mainwindow", "Test QMainWindow"); - parser.addOption(mainWindowOption); - QCommandLineOption standardIconsOption("standard-icons", "Test standard icons"); - parser.addOption(standardIconsOption); - QCommandLineOption cachingOption("caching", "Test caching"); - parser.addOption(cachingOption); - QCommandLineOption styleOption("styles", "Test style"); - parser.addOption(styleOption); - QCommandLineOption fontsOption("fonts", "Test fonts"); - parser.addOption(fontsOption); - QCommandLineOption iconDrawingOption("icondrawing", "Test icon drawing"); - parser.addOption(iconDrawingOption); - QCommandLineOption buttonsOption("buttons", "Test buttons"); - parser.addOption(buttonsOption); + // Default antialiased line + p.setRenderHint(QPainter::Antialiasing); + p.drawLines(linePoints); + + // Cosmetic 1 antialiased line + QPen pen; + pen.setCosmetic(true); + pen.setWidth(1); + p.setPen(pen); + p.translate(3, 3); + p.drawLines(linePoints); + + // Aliased cosmetic 1 line + p.setRenderHint(QPainter::Antialiasing, false); + p.translate(3, 3); + p.drawLines(linePoints); +} - parser.process(app); +void LinePainter::mousePressEvent(QMouseEvent *event) +{ + lastMousePoint = event->pos(); +} - QScopedPointer<PixmapPainter> pixmapPainter; - if (parser.isSet(pixmapPainterOption)) { - pixmapPainter.reset(new PixmapPainter); - pixmapPainter->show(); +void LinePainter::mouseReleaseEvent(QMouseEvent *) +{ + lastMousePoint = QPoint(); +} + +void LinePainter::mouseMoveEvent(QMouseEvent *event) +{ + if (lastMousePoint.isNull()) + return; + + QPoint newMousePoint = event->pos(); + if (lastMousePoint == newMousePoint) + return; + linePoints.append(lastMousePoint); + linePoints.append(newMousePoint); + lastMousePoint = newMousePoint; + update(); +} + +class CursorTester : public QWidget +{ +public: + CursorTester() + :moveLabel(0), moving(false) + { } - QScopedPointer<Labels> label; - if (parser.isSet(labelOption)) { - label.reset(new Labels); - label->resize(200, 200); - label->show(); + inline QRect getRect(int idx) const + { + int h = height() / 2; + return QRect(10, 10 + h * (idx - 1), width() - 20, h - 20); + } + void paintEvent(QPaintEvent *) + { + QPainter p(this); + QRect r1 = getRect(1); + QRect r2 = getRect(2); + p.fillRect(r1, QColor(200, 200, 250)); + p.drawText(r1, "Drag from here to move a window based on QCursor::pos()"); + p.fillRect(r2, QColor(250, 200, 200)); + p.drawText(r2, "Drag from here to move a window based on mouse event position"); + + if (moving) { + p.setPen(Qt::darkGray); + QFont f = font(); + f.setPointSize(8); + p.setFont(f); + p.drawEllipse(mousePos, 30,60); + QPoint pt = mousePos - QPoint(0, 60); + QPoint pt2 = pt - QPoint(30,10); + QPoint offs(30, 0); + p.drawLine(pt, pt2); + p.drawLine(pt2 - offs, pt2 + offs); + p.drawText(pt2 - offs, "mouse pos"); + + p.setPen(QColor(50,130,70)); + QPoint cursorPos = mapFromGlobal(QCursor::pos()); + pt = cursorPos - QPoint(0, 30); + pt2 = pt + QPoint(60, -20); + p.drawEllipse(cursorPos, 60, 30); + p.drawLine(pt, pt2); + p.drawLine(pt2 - offs, pt2 + offs); + p.drawText(pt2 - offs, "cursor pos"); + } } - QScopedPointer<MainWindow> mainWindow; - if (parser.isSet(mainWindowOption)) { - mainWindow.reset(new MainWindow); - mainWindow->show(); + void mousePressEvent(QMouseEvent *e) + { + if (moving) + return; + QRect r1 = getRect(1); + QRect r2 = getRect(2); + + moving = r1.contains(e->pos()) || r2.contains(e->pos()); + if (!moving) + return; + useCursorPos = r1.contains(e->pos()); + + if (!moveLabel) + moveLabel = new QLabel(this,Qt::BypassWindowManagerHint|Qt::FramelessWindowHint|Qt::Window ); + + if (useCursorPos) + moveLabel->setText("I'm following QCursor::pos()"); + else + moveLabel->setText("I'm following QMouseEvent::globalPos()"); + moveLabel->adjustSize(); + mouseMoveEvent(e); + moveLabel->show(); } - QScopedPointer<StandardIcons> icons; - if (parser.isSet(standardIconsOption)) { - icons.reset(new StandardIcons); - icons->resize(510, 510); - icons->show(); + void mouseReleaseEvent(QMouseEvent *) + { + if (moveLabel) + moveLabel->hide(); + update(); + moving = false; } - QScopedPointer<Caching> caching; - if (parser.isSet(cachingOption)) { - caching.reset(new Caching); - caching->resize(300, 300); - caching->show(); + void mouseMoveEvent(QMouseEvent *e) + { + if (!moving) + return; + QPoint pos = useCursorPos ? QCursor::pos() : e->globalPos(); + pos -= moveLabel->rect().center(); + moveLabel->move(pos); + mousePos = e->pos(); + update(); } - QScopedPointer<Style> style; - if (parser.isSet(styleOption)) { - style.reset(new Style); - style->show(); +private: + QLabel *moveLabel; + bool useCursorPos; + bool moving; + QPoint mousePos; +}; + + +class ScreenDisplayer : public QWidget +{ +public: + ScreenDisplayer() + : QWidget(), moveLabel(0), scaleFactor(1.0) + { } - QScopedPointer<Fonts> fonts; - if (parser.isSet(fontsOption)) { - fonts.reset(new Fonts); - fonts->show(); + void timerEvent(QTimerEvent *) { + update(); } - QScopedPointer<IconDrawing> iconDrawing; - if (parser.isSet(iconDrawingOption)) { - iconDrawing.reset(new IconDrawing); - iconDrawing->show(); + void mousePressEvent(QMouseEvent *) { + if (!moveLabel) + moveLabel = new QLabel(this,Qt::BypassWindowManagerHint|Qt::FramelessWindowHint|Qt::Window ); + moveLabel->setText("Hello, Qt this is a label\nwith some text"); + moveLabel->show(); + } + void mouseMoveEvent(QMouseEvent *e) { + if (!moveLabel) + return; + moveLabel->move(e->pos() / scaleFactor); + QString str; + QDebug dbg(&str); + dbg.setAutoInsertSpaces(false); + dbg << moveLabel->geometry(); + moveLabel->setText(str); + } + void mouseReleaseEvent(QMouseEvent *) { + if (moveLabel) + moveLabel->hide(); + } + void showEvent(QShowEvent *) { + refreshTimer.start(300, this); } + void hideEvent(QHideEvent *) { + refreshTimer.stop(); + } + void paintEvent(QPaintEvent *) { + QPainter p(this); + QRectF total; + QList<QScreen*> screens = qApp->screens(); + foreach (QScreen *screen, screens) { + total |= screen->geometry(); + } + if (total.isEmpty()) + return; + + scaleFactor = qMin(width()/total.width(), height()/total.height()); + + p.fillRect(rect(), Qt::black); + p.scale(scaleFactor, scaleFactor); + p.translate(-total.topLeft()); + p.setPen(QPen(Qt::white, 10)); + p.setBrush(Qt::gray); + - QScopedPointer<Buttons> buttons; - if (parser.isSet(buttonsOption)) { - buttons.reset(new Buttons); - buttons->show(); + foreach (QScreen *screen, screens) { + p.drawRect(screen->geometry()); + QFont f = font(); + f.setPixelSize(screen->geometry().height() / 8); + p.setFont(f); + p.drawText(screen->geometry(), Qt::AlignCenter, screen->name()); + } + p.setBrush(QColor(200,220,255,127)); + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (!widget->isHidden()) + p.drawRect(widget->geometry()); + } + + QPolygon cursorShape; + cursorShape << QPoint(0,0) << QPoint(20, 60) + << QPoint(30, 50) << QPoint(60, 80) + << QPoint(80, 60) << QPoint(50, 30) + << QPoint(60, 20); + cursorShape.translate(QCursor::pos()); + p.drawPolygon(cursorShape); } +private: + QLabel *moveLabel; + QBasicTimer refreshTimer; + qreal scaleFactor; +}; + +class PhysicalSizeTest : public QWidget +{ +Q_OBJECT +public: + PhysicalSizeTest() : QWidget(), m_ignoreResize(false) {} + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *) { + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + QSizeF s = size(); + if (!m_ignoreResize) + m_physicalSize = s / ppi; + } + bool event(QEvent *event) { + if (event->type() == QEvent::ScreenChangeInternal) { + // we will get resize events when the scale factor changes + m_ignoreResize = true; + QTimer::singleShot(100, this, SLOT(handleScreenChange())); + } + return QWidget::event(event); + } +public slots: + void handleScreenChange() { + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + QSizeF newSize = m_physicalSize * ppi; + resize(newSize.toSize()); + m_ignoreResize = false; + } +private: + QSizeF m_physicalSize; + bool m_ignoreResize; +}; + +void PhysicalSizeTest::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + qreal ppmm = ppi / 25.4; + qreal h = 15 * ppmm; + QRectF rulerRect(0,0, width(), h); + rulerRect.moveCenter(rect().center()); + + QFont f = font(); + f.setPixelSize(18); + p.setFont(f); + + // draw a rectangle in (Qt) pixel coordinates, for comparison + QRect pixelRect(0, 0, 300, 50); + pixelRect.moveTopLeft(QPoint(5 * ppmm, rulerRect.bottom() + 5 * ppmm)); + p.fillRect(pixelRect, QColor(199,222,255)); + p.drawText(pixelRect, "This rectangle is 300x50 pixels"); + + f.setPixelSize(4 * ppmm); + p.setFont(f); + + QRectF topRect(0, 0, width(), rulerRect.top()); + p.drawText(topRect, Qt::AlignCenter, "The ruler is drawn in physical units.\nThis window tries to keep its physical size\nwhen moved between screens."); + + // draw a ruler in real physical coordinates + + p.fillRect(rulerRect, QColor(255, 222, 111)); + + QPen linePen(Qt::black, 0.3 * ppmm); + p.setPen(linePen); + f.setBold(true); + p.setFont(f); + + qreal vCenter = rulerRect.center().y(); + p.drawLine(0, vCenter, width(), vCenter); + + // cm + for (int i = 0;;) { + i++; + qreal x = i * ppmm; + if (x > width()) + break; + qreal y = rulerRect.bottom(); + qreal len; + if (i % 5) + len = 2 * ppmm; + else if (i % 10) + len = 3 * ppmm; + else + len = h / 2; + + p.drawLine(QPointF(x, y), QPointF(x, y - len)); + if (i % 10 == 5) { + QRectF textR(0, 0, 5 * ppmm, h / 2 - 2 * ppmm); + textR.moveTopLeft(QPointF(x, vCenter)); + int n = i / 10 + 1; + if (n % 10 == 0) + p.setPen(Qt::red); + p.drawText(textR, Qt::AlignCenter, QString::number(n)); + p.setPen(linePen); + } + } + + //inches + for (int i = 0;;) { + i++; + qreal x = i * ppi / 16; + if (x > width()) + break; + qreal y = rulerRect.top(); + + qreal d = h / 10; + qreal len; + if (i % 2) + len = 1 * d; + else if (i % 4) + len = 2 * d; + else if (i % 8) + len = 3 * d; + else if (i % 16) + len = 4 * d; + else + len = h / 2; + + p.drawLine(QPointF(x, y), QPointF(x, y + len)); + if (i % 16 == 12) { + QRectF textR(0, 0, 0.25 * ppi, h / 2 - 2 * d); + textR.moveBottomLeft(QPointF(x, vCenter)); + p.drawText(textR, Qt::AlignCenter, QString::number(1 + i/16)); + } + } + +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + + int argumentCount = QCoreApplication::arguments().count(); + + QCommandLineParser parser; + parser.setApplicationDescription("High DPI tester. Pass one or more of the options to\n" + "test various high-dpi aspects. \n" + "--interactive is a special option and opens a configuration" + " window."); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption controllerOption("interactive", "Show configuration window."); + parser.addOption(controllerOption); + + + DemoContainerList demoList; + demoList << new DemoContainer<PixmapPainter>("pixmap", "Test pixmap painter"); + demoList << new DemoContainer<Labels>("label", "Test Labels"); + demoList << new DemoContainer<MainWindow>("mainwindow", "Test QMainWindow"); + demoList << new DemoContainer<StandardIcons>("standard-icons", "Test standard icons"); + demoList << new DemoContainer<Caching>("caching", "Test caching"); + demoList << new DemoContainer<Style>("styles", "Test style"); + demoList << new DemoContainer<Fonts>("fonts", "Test fonts"); + demoList << new DemoContainer<IconDrawing>("icondrawing", "Test icon drawing"); + demoList << new DemoContainer<Buttons>("buttons", "Test buttons"); + demoList << new DemoContainer<LinePainter>("linepainter", "Test line painting"); + demoList << new DemoContainer<DragWidget>("draganddrop", "Test drag and drop"); + demoList << new DemoContainer<CursorTester>("cursorpos", "Test cursor and window positioning"); + demoList << new DemoContainer<ScreenDisplayer>("screens", "Test screen and window positioning"); + demoList << new DemoContainer<PhysicalSizeTest>("physicalsize", "Test manual highdpi support using physicalDotsPerInch"); + + + foreach (DemoContainerBase *demo, demoList) + parser.addOption(demo->option()); + + parser.process(app); + + //controller takes ownership of all demos + DemoController controller(&demoList, &parser); + + if (parser.isSet(controllerOption) || argumentCount <= 1) + controller.show(); if (QApplication::topLevelWidgets().isEmpty()) parser.showHelp(0); return app.exec(); } + +#include "main.moc" diff --git a/tests/manual/highdpi/qticon16@3x.png b/tests/manual/highdpi/qticon16@3x.png Binary files differnew file mode 100644 index 0000000000..de92658241 --- /dev/null +++ b/tests/manual/highdpi/qticon16@3x.png diff --git a/tests/manual/qcursor/qcursor.pro b/tests/manual/qcursor/qcursor.pro index af082a4b82..0b5c2b1945 100644 --- a/tests/manual/qcursor/qcursor.pro +++ b/tests/manual/qcursor/qcursor.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = allcursors grab_override +SUBDIRS = allcursors grab_override qcursorhighdpi diff --git a/tests/manual/qcursor/qcursorhighdpi/main.cpp b/tests/manual/qcursor/qcursorhighdpi/main.cpp new file mode 100644 index 0000000000..fc45ae00d1 --- /dev/null +++ b/tests/manual/qcursor/qcursorhighdpi/main.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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 <QAction> +#include <QApplication> +#include <QDesktopWidget> +#include <QGridLayout> +#include <QLabel> +#include <QMainWindow> +#include <QMenu> +#include <QMenuBar> +#include <QSharedPointer> +#include <QToolBar> + +#include <QBitmap> +#include <QCursor> +#include <QPainter> +#include <QPixmap> + +#include <QDebug> +#include <QStringList> +#include <QTextStream> + +#if QT_VERSION > 0x050000 +# include <QScreen> +# include <QWindow> +# include <private/qhighdpiscaling_p.h> +# include <qpa/qplatformwindow.h> +#else +# define Q_NULLPTR 0 +# define Q_DECL_OVERRIDE +#endif + +#ifdef Q_OS_WIN +# include <qt_windows.h> +#endif + +#include <algorithm> +#include <iterator> + +// High DPI cursor test for testing cursor sizes in multi-screen setups. +// It creates one widget per screen with a grid of standard cursors, +// pixmap / bitmap cursors and pixmap / bitmap cursors with device pixel ratio 2. +// On the left, there is a ruler with 10 DIP marks. +// The code is meant to compile with Qt 4 also. + +static QString screenInfo(const QWidget *w) +{ + QString result; + QTextStream str(&result); +#if QT_VERSION > 0x050000 + QScreen *screen = Q_NULLPTR; + if (const QWindow *window = w->windowHandle()) + screen = window->screen(); + if (screen) { + str << '"' << screen->name() << "\" " << screen->size().width() << 'x' + << screen->size().height() << ", DPR=" << screen->devicePixelRatio() + << ", " << screen->logicalDotsPerInchX() << "DPI "; + if (QHighDpiScaling::isActive()) + str << ", factor=" << QHighDpiScaling::factor(screen); + else + str << ", no scaling"; + } else { + str << "<null>"; + } +#else + QDesktopWidget *desktop = QApplication::desktop(); + int screenNumber = desktop->screenNumber(w); + str << "Screen #" <<screenNumber << ' ' << desktop->screenGeometry(screenNumber).width() + << 'x' << desktop->screenGeometry(screenNumber).height() << " PD: " << w->logicalDpiX() << "DPI"; +#endif +#ifdef Q_OS_WIN + str << ", SM_C_CURSOR: " << GetSystemMetrics(SM_CXCURSOR) << 'x' << GetSystemMetrics(SM_CYCURSOR); +#endif + return result; +} + +// Helpers for painting pixmaps and creating cursors +static QPixmap paintPixmap(int size, QColor c) +{ + QPixmap result(size, size); + result.fill(c); + QPainter p(&result); + p.drawRect(QRect(QPoint(0, 0), result.size() - QSize(1, 1))); + p.drawLine(0, 0, size, size); + p.drawLine(0, size, size, 0); + return result; +} + +static QCursor pixmapCursor(int size) +{ + QCursor result(paintPixmap(size, Qt::red), size / 2, size / 2); + return result; +} + +static QPair<QBitmap, QBitmap> paintBitmaps(int size) +{ + QBitmap bitmap(size, size); + bitmap.fill(Qt::color1); + QBitmap mask(size, size); + mask.fill(Qt::color1); + { + QPainter mp(&mask); + mp.fillRect(QRect(0, 0, size / 2, size / 2), Qt::color0); + } + return QPair<QBitmap, QBitmap>(bitmap, mask); +} + +static QCursor bitmapCursor(int size) +{ + QPair<QBitmap, QBitmap> bitmaps = paintBitmaps(size); + return QCursor(bitmaps.first, bitmaps.second, size / 2, size / 2); +} + +#if QT_VERSION > 0x050000 +static QCursor pixmapCursorDevicePixelRatio(int size, int dpr) +{ + QPixmap pixmap = paintPixmap(dpr * size, Qt::yellow); + pixmap.setDevicePixelRatio(dpr); + return QCursor(pixmap, size / 2, size / 2); +} + +static QCursor bitmapCursorDevicePixelRatio(int size, int dpr) +{ + QPair<QBitmap, QBitmap> bitmaps = paintBitmaps(dpr * size); + bitmaps.first.setDevicePixelRatio(dpr); + bitmaps.second.setDevicePixelRatio(dpr); + return QCursor(bitmaps.first, bitmaps.second, size / 2, size / 2); +} +#endif // Qt 5 + +// Vertical ruler widget with 10 px marks +class VerticalRuler : public QWidget { +public: + VerticalRuler(QWidget *parent = Q_NULLPTR); + +protected: + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; +}; + +VerticalRuler::VerticalRuler(QWidget *parent) : QWidget(parent) +{ + const int screenWidth = QApplication::desktop()->screenGeometry(this).width(); + setFixedWidth(screenWidth / 48); // 1920 pixel monitor ->40 +} + +void VerticalRuler::paintEvent(QPaintEvent *) +{ + const QSize sizeS(size()); + const QPoint sizeP(sizeS.width(), sizeS.height()); + const QPoint center = sizeP / 2; + QPainter painter(this); + painter.fillRect(QRect(QPoint(0, 0), sizeS), Qt::white); + painter.drawLine(center.x(), 0, center.x(), sizeP.y()); + for (int y = 0; y < sizeP.y(); y += 10) + painter.drawLine(center.x() - 5, y, center.x() + 5, y); +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = Q_NULLPTR); + void updateScreenInfo() { m_screenInfoLabel->setText(screenInfo(this)); } + +public slots: + void screenChanged() { updateScreenInfo(); } + +private: + QLabel *m_screenInfoLabel; +}; + +static QLabel *createCursorLabel(const QCursor &cursor, const QString &additionalText = QString()) +{ + QString labelText; + QDebug(&labelText).nospace() << cursor.shape(); +#if QT_VERSION > 0x050000 + labelText.remove(0, labelText.indexOf('(') + 1); + labelText.chop(1); +#endif // Qt 5 + if (!additionalText.isEmpty()) + labelText += ' ' + additionalText; + QLabel *result = new QLabel(labelText); + result->setFrameShape(QFrame::Box); + result->setCursor(cursor); + return result; +} + +static void addToGrid(QWidget *w, QGridLayout *gridLayout, int columnCount, int &row, int &col) +{ + gridLayout->addWidget(w, row, col); + if (col >= columnCount) { + col = 0; + row++; + } else { + col++; + } +} + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , m_screenInfoLabel(new QLabel) +{ + QString title = "Cursors "; +#if QT_VERSION > 0x050000 + title += '(' + QGuiApplication::platformName() + ") "; +#endif + title += QT_VERSION_STR; + setWindowTitle(title); + + QMenu *fileMenu = menuBar()->addMenu("File"); + QAction *quitAction = fileMenu->addAction("Quit"); + quitAction->setShortcut(Qt::CTRL + Qt::Key_Q); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + + QToolBar *fileToolBar = addToolBar("File"); + fileToolBar->addAction(quitAction); + + QWidget *cw = new QWidget; + QHBoxLayout *hLayout = new QHBoxLayout(cw); + hLayout->addWidget(new VerticalRuler(cw)); + QGridLayout *gridLayout = new QGridLayout; + hLayout->addLayout(gridLayout); + + const int columnCount = 5; + const int size = 32; + + int row = 0; + int col = 0; + for (int i = 0; i < Qt::BitmapCursor; ++i) + addToGrid(createCursorLabel(QCursor(static_cast<Qt::CursorShape>(i))), gridLayout, columnCount, row, col); + + addToGrid(createCursorLabel(QCursor(pixmapCursor(size)), + QLatin1String("Plain PX ") + QString::number(size)), + gridLayout, columnCount, row, col); + + addToGrid(createCursorLabel(bitmapCursor(size), + QLatin1String("Plain BM ") + QString::number(size)), + gridLayout, columnCount, row, col); + +#if QT_VERSION > 0x050000 + addToGrid(createCursorLabel(QCursor(pixmapCursorDevicePixelRatio(size, 2)), + "PX with DPR 2 " + QString::number(size)), + gridLayout, columnCount, row, col); + + addToGrid(createCursorLabel(QCursor(bitmapCursorDevicePixelRatio(size, 2)), + "BM with DPR 2 " + QString::number(size)), + gridLayout, columnCount, row, col); +#endif // Qt 5 + + gridLayout->addWidget(m_screenInfoLabel, row + 1, 0, 1, columnCount); + + setCentralWidget(cw); +} + +typedef QSharedPointer<MainWindow> MainWindowPtr; +typedef QList<MainWindowPtr> MainWindowPtrList; + +int main(int argc, char *argv[]) +{ + QStringList arguments; + std::copy(argv + 1, argv + argc, std::back_inserter(arguments)); + +#if QT_VERSION > 0x050000 + if (arguments.contains("-s")) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + else if (arguments.contains("-n")) + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); +#endif // Qt 5 + + QApplication app(argc, argv); + + MainWindowPtrList windows; + + QDesktopWidget *desktopWidget = app.desktop(); + + for (int s = desktopWidget->screenCount() - 1; s >= 0; --s) { + MainWindowPtr window(new MainWindow(desktopWidget->screen(s))); + const QPoint pos = desktopWidget->screenGeometry(s).center() - QPoint(200, 100); + window->move(pos); + windows.append(window); + window->show(); + window->updateScreenInfo(); +#if QT_VERSION > 0x050000 + QObject::connect(window->windowHandle(), &QWindow::screenChanged, + window.data(), &MainWindow::updateScreenInfo); +#endif + } + return app.exec(); +} +#include "main.moc" diff --git a/tests/manual/qcursor/qcursorhighdpi/qcursorhighdpi.pro b/tests/manual/qcursor/qcursorhighdpi/qcursorhighdpi.pro new file mode 100644 index 0000000000..3a8fc25b33 --- /dev/null +++ b/tests/manual/qcursor/qcursorhighdpi/qcursorhighdpi.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +QT = core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += gui-private core-private widgets +CONFIG -= app_bundle +SOURCES += main.cpp +win32: LIBS += -lUser32 diff --git a/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp b/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp index 04c9b3f72c..2792f6f1a3 100644 --- a/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp +++ b/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp @@ -61,6 +61,7 @@ QOpenGLTextureBlitWindow::QOpenGLTextureBlitWindow() m_context->makeCurrent(this); m_blitter.create(); + qDebug("GL_TEXTURE_EXTERNAL_OES support: %d", m_blitter.supportsExternalOESTarget()); } void QOpenGLTextureBlitWindow::render() @@ -132,6 +133,12 @@ void QOpenGLTextureBlitWindow::render() m_blitter.setSwizzleRB(false); m_blitter.release(); + if (m_blitter.supportsExternalOESTarget()) { + // Cannot do much testing here, just verify that bind and release work, meaning that the program is present. + m_blitter.bind(0x8D65); + m_blitter.release(); + } + m_context->swapBuffers(this); } diff --git a/tests/manual/qopenglwidget/openglwidget/main.cpp b/tests/manual/qopenglwidget/openglwidget/main.cpp index aaa48ea60a..a56cea1dfe 100644 --- a/tests/manual/qopenglwidget/openglwidget/main.cpp +++ b/tests/manual/qopenglwidget/openglwidget/main.cpp @@ -35,13 +35,108 @@ #include <QApplication> #include <QPushButton> #include <QMdiArea> +#include <QMdiSubWindow> +#include <QMenu> +#include <QMenuBar> +#include <QMainWindow> #include <QLCDNumber> +#include <QScrollArea> +#include <QScrollBar> +#include <QTabWidget> +#include <QLabel> #include <QTimer> #include <QSurfaceFormat> #include <QDebug> +#include <private/qwindow_p.h> + +class Tools : public QObject +{ + Q_OBJECT + +public: + Tools(QWidget *root, QWidget *widgetToTurn, const QVector<QWidget *> glwidgets) + : m_root(root), m_widgetToTurn(widgetToTurn), m_glWidgets(glwidgets) { } + void dump(); + +private slots: + void turnNative(); + void hideShowAllGL(); + void dumpCompositingStatus(); + +signals: + void aboutToShowGLWidgets(); + +private: + void dumpWidget(QWidget *w, int indent = 0); + + QWidget *m_root; + QWidget *m_widgetToTurn; + QVector<QWidget *> m_glWidgets; +}; + +void Tools::turnNative() +{ + qDebug("Turning into native"); + m_widgetToTurn->winId(); + dump(); +} + +void Tools::hideShowAllGL() +{ + if (m_glWidgets[0]->isVisible()) { + qDebug("Hiding all render-to-texture widgets"); + foreach (QWidget *w, m_glWidgets) + w->hide(); + } else { + qDebug("Showing all render-to-texture widgets"); + emit aboutToShowGLWidgets(); + foreach (QWidget *w, m_glWidgets) + w->show(); + } +} + +void Tools::dump() +{ + qDebug() << "Widget hierarchy"; + dumpWidget(m_root); + qDebug() << "========"; +} + +void Tools::dumpWidget(QWidget *w, int indent) +{ + QString indentStr; + indentStr.fill(' ', indent); + qDebug().noquote() << indentStr << w << "winId =" << w->internalWinId(); + foreach (QObject *obj, w->children()) { + if (QWidget *cw = qobject_cast<QWidget *>(obj)) + dumpWidget(cw, indent + 4); + } +} + +void Tools::dumpCompositingStatus() +{ + QWindow *w = m_root->window()->windowHandle(); + qDebug() << "Compositing status for" << w << m_root->window() << "is" << QWindowPrivate::get(w)->compositing; +} + +class TabWidgetResetter : public QObject +{ + Q_OBJECT +public: + TabWidgetResetter(QTabWidget *tw) : m_tw(tw) { } +public slots: + void reset() { m_tw->setCurrentIndex(0); } +private: + QTabWidget *m_tw; +}; int main(int argc, char *argv[]) { + if (argc > 1 && !strcmp(argv[1], "--sharecontext")) { + qDebug("Requesting all contexts to share"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + } + QApplication a(argc, argv); QSurfaceFormat format; @@ -53,28 +148,86 @@ int main(int argc, char *argv[]) } qDebug() << "Requesting" << format; - QMdiArea w; - w.resize(400,400); + QMainWindow wnd; + wnd.setObjectName("Main Window"); + wnd.resize(1024, 768); + + QMdiArea *w = new QMdiArea; + w->setObjectName("MDI area"); + w->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + wnd.setCentralWidget(w); - OpenGLWidget *glw = new OpenGLWidget; + OpenGLWidget *glw = new OpenGLWidget(33, QVector3D(0, 0, 1)); + glw->setObjectName("First GL Widget with 33 ms timer"); glw->setFormat(format); - w.addSubWindow(glw); - glw->setMinimumSize(100,100); + glw->setMinimumSize(100, 100); + QMdiSubWindow *sw = w->addSubWindow(glw); + sw->setObjectName("First MDI Sub-Window"); + sw->setWindowTitle("33 ms timer"); - OpenGLWidget *glw2 = new OpenGLWidget; + OpenGLWidget *glw2 = new OpenGLWidget(16); + glw2->setObjectName("Second GL Widget with 16 ms timer"); glw2->setFormat(format); - glw2->setMinimumSize(100,100); - w.addSubWindow(glw2); + glw2->setMinimumSize(100, 100); + QOpenGLWidget *glw22 = new OpenGLWidget(16); + glw22->setObjectName("Second #2 GLWidget"); + glw22->setParent(glw2); + glw22->resize(40, 40); + sw = w->addSubWindow(glw2); + sw->setObjectName("Second MDI Sub-Window"); + sw->setWindowTitle("16 ms timer"); + + OpenGLWidget *glw3 = new OpenGLWidget(0); // trigger updates continuously, no timer + glw3->setObjectName("GL widget in scroll area (possibly native)"); + glw3->setFormat(format); + glw3->setFixedSize(600, 600); + QScrollArea *sa = new QScrollArea; + sa->setWidget(glw3); + sa->setMinimumSize(100, 100); + sa->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + sw = w->addSubWindow(sa); + sw->setObjectName("MDI Sub-Window for scroll area"); + sw->setWindowTitle("Cont. update"); + sw->resize(300, 300); + sa->verticalScrollBar()->setValue(300); QLCDNumber *lcd = new QLCDNumber; lcd->display(1337); - lcd->setMinimumSize(300,100); - w.addSubWindow(lcd); + lcd->setMinimumSize(300, 100); + sw = w->addSubWindow(lcd); + sw->setObjectName("MDI Sub-Window for LCD widget"); + sw->setWindowTitle("Ordinary widget"); + + QTabWidget *tw = new QTabWidget; + QOpenGLWidget *glw4 = new OpenGLWidget(16, QVector3D(1, 0, 0)); + glw4->setObjectName("GL widget in tab widget"); + tw->addTab(glw4, "OpenGL"); + QLabel *label = new QLabel("Another tab"); + tw->addTab(label, "Not OpenGL"); + tw->setMinimumSize(100, 100); + sw = w->addSubWindow(tw); + sw->setObjectName("MDI Sub-Window for tab widget"); + sw->setWindowTitle("Tabs"); - w.show(); + TabWidgetResetter twr(tw); + Tools t(&wnd, glw3, QVector<QWidget *>() << glw << glw2 << glw3 << glw4); + QObject::connect(&t, SIGNAL(aboutToShowGLWidgets()), &twr, SLOT(reset())); + QMenu *toolsMenu = wnd.menuBar()->addMenu("&Tools"); + toolsMenu->addAction("&Turn widgets (or some parent) into native", &t, SLOT(turnNative())); + toolsMenu->addAction("&Hide/show all OpenGL widgets", &t, SLOT(hideShowAllGL())); + + QTimer compStatusDumpTimer; + QObject::connect(&compStatusDumpTimer, SIGNAL(timeout()), &t, SLOT(dumpCompositingStatus())); + compStatusDumpTimer.start(5000); + + wnd.show(); if (glw->isValid()) qDebug() << "Got" << glw->format(); + t.dump(); + return a.exec(); } + +#include "main.moc" diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp index d47e12edc8..4d2463b84d 100644 --- a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp +++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp @@ -75,16 +75,23 @@ public: int w,h; QWidget *q; + + int m_interval; + QVector3D m_rotAxis; }; -OpenGLWidget::OpenGLWidget(QWidget *parent) +OpenGLWidget::OpenGLWidget(int interval, const QVector3D &rotAxis, QWidget *parent) : QOpenGLWidget(parent) { - d = new OpenGLWidgetPrivate(this); - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(30); + d.reset(new OpenGLWidgetPrivate(this)); + d->m_interval = interval; + d->m_rotAxis = rotAxis; + if (interval > 0) { + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(interval); + } } OpenGLWidget::~OpenGLWidget() @@ -152,7 +159,8 @@ void OpenGLWidgetPrivate::render() QMatrix4x4 matrix; matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f); matrix.translate(0, 0, -2); - matrix.rotate(100.0f * m_frame / 30/*screen()->refreshRate()*/, 0, 1, 0); + const qreal angle = 100.0f * m_frame / 30; + matrix.rotate(angle, m_rotAxis); m_program->setUniformValue(m_matrixUniform, matrix); @@ -182,4 +190,7 @@ void OpenGLWidgetPrivate::render() m_program->release(); ++m_frame; + + if (m_interval <= 0) + q->update(); } diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.h b/tests/manual/qopenglwidget/openglwidget/openglwidget.h index 4dc5fde067..a1d5490845 100644 --- a/tests/manual/qopenglwidget/openglwidget/openglwidget.h +++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.h @@ -35,13 +35,14 @@ #define OPENGLWIDGET_H #include <QtWidgets/QOpenGLWidget> +#include <QtGui/QVector3D> class OpenGLWidgetPrivate; class OpenGLWidget : public QOpenGLWidget { Q_OBJECT public: - OpenGLWidget(QWidget *parent = 0); + OpenGLWidget(int interval = 30, const QVector3D &rotAxis = QVector3D(0, 1, 0), QWidget *parent = 0); ~OpenGLWidget(); void initializeGL(); @@ -49,7 +50,7 @@ public: void paintGL(); private: - OpenGLWidgetPrivate *d; + QScopedPointer<OpenGLWidgetPrivate> d; }; #endif // OPENGLWIDGET_H diff --git a/tests/manual/qscreen/main.cpp b/tests/manual/qscreen/main.cpp index 1047ffcdfc..298996a59b 100644 --- a/tests/manual/qscreen/main.cpp +++ b/tests/manual/qscreen/main.cpp @@ -36,23 +36,127 @@ #include <QScreen> #include <QWindow> #include <QDebug> +#include <QTextStream> #include <QFormLayout> +#include <QMainWindow> +#include <QMenu> +#include <QMenuBar> +#include <QAction> +#include <QStatusBar> #include <QLineEdit> #include <QDesktopWidget> -int i = 0; +class ScreenPropertyWatcher : public PropertyWatcher +{ + Q_OBJECT +public: + ScreenPropertyWatcher(QWidget *wp = Q_NULLPTR) : PropertyWatcher(Q_NULLPTR, QString(), wp) + { + // workaround for the fact that virtualSiblings is not a property, + // thus there is no change notification: + // allow the user to update the field manually + connect(this, &PropertyWatcher::updatedAllFields, this, &ScreenPropertyWatcher::updateSiblings); + } + + QScreen *screenSubject() const { return qobject_cast<QScreen *>(subject()); } + void setScreenSubject(QScreen *s, const QString &annotation = QString()) + { + setSubject(s, annotation); + updateSiblings(); + } -typedef QHash<QScreen*, PropertyWatcher*> ScreensHash; -Q_GLOBAL_STATIC(ScreensHash, props); +public slots: + void updateSiblings(); +}; -void updateSiblings(PropertyWatcher* w) +void ScreenPropertyWatcher::updateSiblings() { - QLineEdit *siblingsField = w->findChild<QLineEdit *>("siblings"); - QScreen* screen = (QScreen*)w->subject(); - QStringList siblingsList; - foreach (QScreen *sibling, screen->virtualSiblings()) - siblingsList << sibling->name(); - siblingsField->setText(siblingsList.join(", ")); + const QScreen *screen = screenSubject(); + if (!screen) + return; + const QString objectName = QLatin1String("siblings"); + QLineEdit *siblingsField = findChild<QLineEdit *>(objectName); + if (!siblingsField) { + siblingsField = new QLineEdit(this); + siblingsField->setObjectName(objectName); + siblingsField->setReadOnly(true); + formLayout()->insertRow(0, QLatin1String("virtualSiblings"), siblingsField); + } + QString text; + foreach (const QScreen *sibling, screen->virtualSiblings()) { + if (!text.isEmpty()) + text += QLatin1String(", "); + text += sibling->name(); + } + siblingsField->setText(text); +} + +class ScreenWatcherMainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit ScreenWatcherMainWindow(QScreen *screen); + + QScreen *screenSubject() const { return m_watcher->screenSubject(); } + +protected: + bool event(QEvent *event) Q_DECL_OVERRIDE; + +private: + const QString m_annotation; + ScreenPropertyWatcher *m_watcher; +}; + +static int i = 0; + +ScreenWatcherMainWindow::ScreenWatcherMainWindow(QScreen *screen) + : m_annotation(QLatin1Char('#') + QString::number(i++)) + , m_watcher(new ScreenPropertyWatcher(this)) +{ + setAttribute(Qt::WA_DeleteOnClose); + setCentralWidget(m_watcher); + m_watcher->setScreenSubject(screen, m_annotation); + + QMenu *fileMenu = menuBar()->addMenu(QLatin1String("&File")); + QAction *a = fileMenu->addAction(QLatin1String("Close")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_W)); + connect(a, SIGNAL(triggered()), this, SLOT(close())); + a = fileMenu->addAction(QLatin1String("Quit")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); + connect(a, SIGNAL(triggered()), qApp, SLOT(quit())); +} + +static inline QString msgScreenChange(const QWidget *w, const QScreen *oldScreen, const QScreen *newScreen) +{ + QString result; + const QRect geometry = w->geometry(); + const QPoint pos = QCursor::pos(); + if (!newScreen) { + result = QLatin1String("Screen changed --> null"); + } else if (!oldScreen) { + QTextStream(&result) << "Screen changed null --> \"" + << newScreen->name() << "\" at " << pos.x() << ',' << pos.y() << " geometry: " + << geometry.width() << 'x' << geometry.height() << forcesign << geometry.x() + << geometry.y() << '.'; + } else { + QTextStream(&result) << "Screen changed \"" << oldScreen->name() << "\" --> \"" + << newScreen->name() << "\" at " << pos.x() << ',' << pos.y() << " geometry: " + << geometry.width() << 'x' << geometry.height() << forcesign << geometry.x() + << geometry.y() << '.'; + } + return result; +} + +bool ScreenWatcherMainWindow::event(QEvent *event) +{ + if (event->type() == QEvent::ScreenChangeInternal) { + QScreen *newScreen = windowHandle()->screen(); + const QString message = msgScreenChange(this, m_watcher->screenSubject(), newScreen); + qDebug().noquote() << message; + statusBar()->showMessage(message); + m_watcher->setScreenSubject(newScreen, m_annotation); + } + return QMainWindow::event(event); } void screenAdded(QScreen* screen) @@ -60,12 +164,7 @@ void screenAdded(QScreen* screen) screen->setOrientationUpdateMask((Qt::ScreenOrientations)0x0F); qDebug("\nscreenAdded %s siblings %d first %s", qPrintable(screen->name()), screen->virtualSiblings().count(), (screen->virtualSiblings().isEmpty() ? "none" : qPrintable(screen->virtualSiblings().first()->name()))); - PropertyWatcher *w = new PropertyWatcher(screen, QString::number(i++)); - QLineEdit *siblingsField = new QLineEdit(); - siblingsField->setObjectName("siblings"); - siblingsField->setReadOnly(true); - w->layout()->insertRow(0, "virtualSiblings", siblingsField); - updateSiblings(w); + ScreenWatcherMainWindow *w = new ScreenWatcherMainWindow(screen); // Set the screen via QDesktopWidget. This corresponds to setScreen() for the underlying // QWindow. This is essential when having separate X screens since the the positioning below is @@ -84,18 +183,17 @@ void screenAdded(QScreen* screen) geom.setHeight(screen->geometry().height() * 9 / 10); geom.moveCenter(screen->geometry().center()); w->setGeometry(geom); - - props->insert(screen, w); - - // workaround for the fact that virtualSiblings is not a property, - // thus there is no change notification: - // allow the user to update the field manually - QObject::connect(w, &PropertyWatcher::updatedAllFields, &updateSiblings); } void screenRemoved(QScreen* screen) { - delete props->take(screen); + const QWidgetList topLevels = QApplication::topLevelWidgets(); + for (int i = topLevels.size() - 1; i >= 0; --i) { + if (ScreenWatcherMainWindow *sw = qobject_cast<ScreenWatcherMainWindow *>(topLevels.at(i))) { + if (sw->screenSubject() == screen) + sw->close(); + } + } } int main(int argc, char *argv[]) @@ -108,3 +206,5 @@ int main(int argc, char *argv[]) QObject::connect((const QGuiApplication*)QGuiApplication::instance(), &QGuiApplication::screenRemoved, &screenRemoved); return a.exec(); } + +#include "main.moc" diff --git a/tests/manual/qscreen/propertywatcher.cpp b/tests/manual/qscreen/propertywatcher.cpp index cfb5ea272d..b745ef5125 100644 --- a/tests/manual/qscreen/propertywatcher.cpp +++ b/tests/manual/qscreen/propertywatcher.cpp @@ -35,33 +35,82 @@ #include <QMetaProperty> #include <QFormLayout> #include <QPushButton> +#include <QLabel> #include "propertyfield.h" PropertyWatcher::PropertyWatcher(QObject *subject, QString annotation, QWidget *parent) - : QWidget(parent), m_subject(subject), m_layout(new QFormLayout) + : QWidget(parent), m_subject(Q_NULLPTR), m_formLayout(new QFormLayout(this)) { - setWindowTitle(QString("Properties of %1 %2 %3") - .arg(subject->metaObject()->className()).arg(subject->objectName()).arg(annotation)); setMinimumSize(450, 300); + m_formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + setSubject(subject, annotation); +} + +class UpdatesEnabledBlocker +{ + Q_DISABLE_COPY(UpdatesEnabledBlocker); +public: + explicit UpdatesEnabledBlocker(QWidget *w) : m_widget(w) + { + m_widget->setUpdatesEnabled(false); + } + ~UpdatesEnabledBlocker() + { + m_widget->setUpdatesEnabled(true); + m_widget->update(); + } + +private: + QWidget *m_widget; +}; + +void PropertyWatcher::setSubject(QObject *s, const QString &annotation) +{ + if (s == m_subject) + return; + + UpdatesEnabledBlocker blocker(this); + + if (m_subject) { + disconnect(m_subject, &QObject::destroyed, this, &PropertyWatcher::subjectDestroyed); + for (int i = m_formLayout->count() - 1; i >= 0; --i) { + QLayoutItem *item = m_formLayout->takeAt(i); + delete item->widget(); + delete item; + } + window()->setWindowTitle(QString()); + window()->setWindowIconText(QString()); + } + + m_subject = s; + if (!m_subject) + return; + const QMetaObject* meta = m_subject->metaObject(); + QString title = QLatin1String("Properties ") + QLatin1String(meta->className()); + if (!m_subject->objectName().isEmpty()) + title += QLatin1Char(' ') + m_subject->objectName(); + if (!annotation.isEmpty()) + title += QLatin1Char(' ') + annotation; + window()->setWindowTitle(title); - for (int i = 0; i < meta->propertyCount(); ++i) { - QMetaProperty prop = meta->property(i); + for (int i = 0, count = meta->propertyCount(); i < count; ++i) { + const QMetaProperty prop = meta->property(i); if (prop.isReadable()) { - PropertyField* field = new PropertyField(m_subject, prop); - m_layout->addRow(prop.name(), field); + QLabel *label = new QLabel(prop.name(), this); + PropertyField *field = new PropertyField(m_subject, prop, this); + m_formLayout->addRow(label, field); + if (!qstrcmp(prop.name(), "name")) + window()->setWindowIconText(prop.read(m_subject).toString()); + label->setVisible(true); + field->setVisible(true); } } - QPushButton *updateButton = new QPushButton("update"); - connect(updateButton, &QPushButton::clicked, this, &PropertyWatcher::updateAllFields); - m_layout->addRow("", updateButton); - m_layout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); - setLayout(m_layout); - connect(subject, &QObject::destroyed, this, &PropertyWatcher::subjectDestroyed); -} + connect(m_subject, &QObject::destroyed, this, &PropertyWatcher::subjectDestroyed); -PropertyWatcher::~PropertyWatcher() -{ + QPushButton *updateButton = new QPushButton(QLatin1String("Update"), this); + connect(updateButton, &QPushButton::clicked, this, &PropertyWatcher::updateAllFields); + m_formLayout->addRow(QString(), updateButton); } void PropertyWatcher::updateAllFields() diff --git a/tests/manual/qscreen/propertywatcher.h b/tests/manual/qscreen/propertywatcher.h index 7dccfe3672..01e448845a 100644 --- a/tests/manual/qscreen/propertywatcher.h +++ b/tests/manual/qscreen/propertywatcher.h @@ -44,10 +44,12 @@ class PropertyWatcher : public QWidget Q_OBJECT public: - PropertyWatcher(QObject* subject, QString annotation = QString(), QWidget *parent = 0); - ~PropertyWatcher(); - QFormLayout *layout() { return m_layout; } - QObject* subject() { return m_subject; } + explicit PropertyWatcher(QObject* subject = Q_NULLPTR, QString annotation = QString(), QWidget *parent = Q_NULLPTR); + + QFormLayout *formLayout() { return m_formLayout; } + + QObject *subject() const { return m_subject; } + void setSubject(QObject *s, const QString &annotation = QString()); public slots: void updateAllFields(); @@ -56,9 +58,9 @@ public slots: signals: void updatedAllFields(PropertyWatcher* sender); -protected: +private: QObject* m_subject; - QFormLayout * m_layout; + QFormLayout * m_formLayout; }; #endif // PROPERTY_WATCHER_H diff --git a/tests/manual/qscreen/qscreen.pro b/tests/manual/qscreen/qscreen.pro index cec8bbf245..5d587db3f4 100644 --- a/tests/manual/qscreen/qscreen.pro +++ b/tests/manual/qscreen/qscreen.pro @@ -1,4 +1,5 @@ QT += core gui widgets +CONFIG += console TARGET = qscreen TEMPLATE = app diff --git a/tests/manual/qsysinfo/main.cpp b/tests/manual/qsysinfo/main.cpp index a3f21140cb..9456bd9b03 100644 --- a/tests/manual/qsysinfo/main.cpp +++ b/tests/manual/qsysinfo/main.cpp @@ -134,6 +134,7 @@ int main(int argc, char *argv[]) printf("QSysInfo::productType() = %s\n", qPrintable(QSysInfo::productType())); printf("QSysInfo::productVersion() = %s\n", qPrintable(QSysInfo::productVersion())); printf("QSysInfo::prettyProductName() = %s\n", qPrintable(QSysInfo::prettyProductName())); + printf("QSysInfo::machineHostName() = %s\n", qPrintable(QSysInfo::machineHostName())); return 0; } diff --git a/tests/manual/qtabletevent/device_information/tabletwidget.cpp b/tests/manual/qtabletevent/device_information/tabletwidget.cpp index 2e4cb6658f..f1d838f01d 100644 --- a/tests/manual/qtabletevent/device_information/tabletwidget.cpp +++ b/tests/manual/qtabletevent/device_information/tabletwidget.cpp @@ -73,6 +73,7 @@ bool TabletWidget::eventFilter(QObject *, QEvent *ev) mRot = event->rotation(); mButton = event->button(); mButtons = event->buttons(); + mTimestamp = event->timestamp(); if (isVisible()) update(); break; @@ -84,6 +85,7 @@ bool TabletWidget::eventFilter(QObject *, QEvent *ev) mType = event->type(); mPos = event->pos(); mGPos = event->globalPos(); + mTimestamp = event->timestamp(); } default: break; @@ -122,6 +124,7 @@ void TabletWidget::paintEvent(QPaintEvent *) eventInfo << QString("Global position: %1 %2").arg(QString::number(mGPos.x()), QString::number(mGPos.y())); eventInfo << QString("Local position: %1 %2").arg(QString::number(mPos.x()), QString::number(mPos.y())); + eventInfo << QString("Timestamp: %1").arg(QString::number(mTimestamp)); if (mType == QEvent::TabletEnterProximity || mType == QEvent::TabletLeaveProximity || mType == QEvent::TabletMove || mType == QEvent::TabletPress || mType == QEvent::TabletRelease) { diff --git a/tests/manual/qtabletevent/device_information/tabletwidget.h b/tests/manual/qtabletevent/device_information/tabletwidget.h index 2861eb4814..95631be57b 100644 --- a/tests/manual/qtabletevent/device_information/tabletwidget.h +++ b/tests/manual/qtabletevent/device_information/tabletwidget.h @@ -65,6 +65,7 @@ private: qreal mPress, mTangential, mRot; qint64 mUnique; bool mMouseToo; + ulong mTimestamp; }; #endif // TABLETWIDGET_H diff --git a/tests/manual/qtabletevent/regular_widgets/main.cpp b/tests/manual/qtabletevent/regular_widgets/main.cpp index 5a83decfa2..a6fddd4b18 100644 --- a/tests/manual/qtabletevent/regular_widgets/main.cpp +++ b/tests/manual/qtabletevent/regular_widgets/main.cpp @@ -39,6 +39,7 @@ #include <QMenuBar> #include <QMenu> #include <QAction> +#include <QStatusBar> #include <QVector> #include <QPainter> #include <QCursor> @@ -72,6 +73,9 @@ public: public slots: void clearPoints() { m_points.clear(); update(); } +signals: + void stats(QString s); + protected: void mouseDoubleClickEvent(QMouseEvent *event) { outputMouseEvent(event); } void mouseMoveEvent(QMouseEvent *event) { outputMouseEvent(event); } @@ -81,6 +85,7 @@ protected: void tabletEvent(QTabletEvent *); void paintEvent(QPaintEvent *); + void timerEvent(QTimerEvent *); private: void outputMouseEvent(QMouseEvent *event); @@ -89,28 +94,36 @@ private: bool m_lastIsTabletMove; Qt::MouseButton m_lastButton; QVector<TabletPoint> m_points; + int m_tabletMoveCount; + int m_paintEventCount; }; EventReportWidget::EventReportWidget() : m_lastIsMouseMove(false) , m_lastIsTabletMove(false) , m_lastButton(Qt::NoButton) -{ } + , m_tabletMoveCount(0) + , m_paintEventCount(0) +{ + startTimer(1000); +} void EventReportWidget::paintEvent(QPaintEvent *) { QPainter p(this); + int lineSpacing = fontMetrics().lineSpacing(); + int halfLineSpacing = lineSpacing / 2; const QRectF geom = QRectF(QPoint(0, 0), size()); p.fillRect(geom, Qt::white); p.drawRect(QRectF(geom.topLeft(), geom.bottomRight() - QPointF(1,1))); p.setPen(Qt::white); QPainterPath ellipse; - ellipse.addEllipse(0, 0, 50, 10); + ellipse.addEllipse(0, 0, halfLineSpacing * 5, halfLineSpacing); foreach (const TabletPoint &t, m_points) { if (geom.contains(t.pos)) { QPainterPath pp; - pp.addEllipse(t.pos, 8, 8); - QRectF pointBounds(t.pos.x() - 10, t.pos.y() - 10, 20, 20); + pp.addEllipse(t.pos, halfLineSpacing, halfLineSpacing); + QRectF pointBounds(t.pos.x() - halfLineSpacing, t.pos.y() - halfLineSpacing, lineSpacing, lineSpacing); switch (t.type) { case TabletButtonPress: p.fillPath(pp, Qt::darkGreen); @@ -133,7 +146,7 @@ void EventReportWidget::paintEvent(QPaintEvent *) p.drawPath(ellipse); p.restore(); } else { - p.drawEllipse(t.pos, t.pressure * 10.0, t.pressure * 10.0); + p.drawEllipse(t.pos, t.pressure * halfLineSpacing, t.pressure * halfLineSpacing); } p.setPen(Qt::white); } else { @@ -143,6 +156,7 @@ void EventReportWidget::paintEvent(QPaintEvent *) } } } + ++m_paintEventCount; } void EventReportWidget::tabletEvent(QTabletEvent *event) @@ -152,11 +166,13 @@ void EventReportWidget::tabletEvent(QTabletEvent *event) switch (event->type()) { case QEvent::TabletEnterProximity: case QEvent::TabletLeaveProximity: + qDebug() << "proximity" << event; break; case QEvent::TabletMove: m_points.push_back(TabletPoint(event->pos(), TabletMove, m_lastButton, event->pointerType(), event->pressure(), event->rotation())); update(); isMove = true; + ++m_tabletMoveCount; break; case QEvent::TabletPress: m_points.push_back(TabletPoint(event->pos(), TabletButtonPress, event->button(), event->pointerType(), event->rotation())); @@ -192,6 +208,13 @@ void EventReportWidget::outputMouseEvent(QMouseEvent *event) qDebug() << event; } +void EventReportWidget::timerEvent(QTimerEvent *) +{ + emit stats(QString("%1 moves/sec, %2 frames/sec").arg(m_tabletMoveCount).arg(m_paintEventCount)); + m_tabletMoveCount = 0; + m_paintEventCount = 0; +} + int main(int argc, char *argv[]) { QApplication app(argc, argv); @@ -201,6 +224,7 @@ int main(int argc, char *argv[]) widget->setMinimumSize(640, 480); QMenu *fileMenu = mainWindow.menuBar()->addMenu("File"); QObject::connect(fileMenu->addAction("Clear"), SIGNAL(triggered()), widget, SLOT(clearPoints())); + QObject::connect(widget, SIGNAL(stats(QString)), mainWindow.statusBar(), SLOT(showMessage(QString))); QAction *quitAction = fileMenu->addAction("Quit"); QObject::connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); quitAction->setShortcut(Qt::CTRL + Qt::Key_Q); diff --git a/tests/manual/touch/main.cpp b/tests/manual/touch/main.cpp index e1114d7f57..b7029767c9 100644 --- a/tests/manual/touch/main.cpp +++ b/tests/manual/touch/main.cpp @@ -32,21 +32,187 @@ ****************************************************************************/ #include <QApplication> +#include <QGesture> #include <QLabel> #include <QMenu> #include <QMenuBar> #include <QAction> #include <QMainWindow> #include <QSplitter> +#include <QToolBar> #include <QVector> #include <QCommandLineOption> #include <QCommandLineParser> #include <QPlainTextEdit> +#include <QPainter> +#include <QPainterPath> #include <QPaintEvent> #include <QScreen> +#include <QSharedPointer> #include <QDebug> #include <QTextStream> +bool optIgnoreTouch = false; +QVector<Qt::GestureType> optGestures; + +static inline void drawCircle(const QPointF ¢er, qreal radius, const QColor &color, QPainter &painter) +{ + const QPen oldPen = painter.pen(); + QPen pen = oldPen; + pen.setColor(color); + painter.setPen(pen); + painter.drawEllipse(center, radius, radius); + painter.setPen(oldPen); +} + +static inline void fillCircle(const QPointF ¢er, qreal radius, const QColor &color, QPainter &painter) +{ + QPainterPath painterPath; + painterPath.addEllipse(center, radius, radius); + painter.fillPath(painterPath, color); +} + +// Draws an arrow assuming a mathematical coordinate system, Y axis pointing +// upwards, angle counterclockwise (that is, 45' is pointing up/right). +static void drawArrow(const QPointF ¢er, qreal length, qreal angleDegrees, + const QColor &color, int arrowSize, QPainter &painter) +{ + painter.save(); + painter.translate(center); // Transform center to (0,0) rotate and draw arrow pointing right. + painter.rotate(-angleDegrees); + QPen pen = painter.pen(); + pen.setColor(color); + pen.setWidth(2); + painter.setPen(pen); + const QPointF endPoint(length, 0); + painter.drawLine(QPointF(0, 0), endPoint); + painter.drawLine(endPoint, endPoint + QPoint(-arrowSize, -arrowSize)); + painter.drawLine(endPoint, endPoint + QPoint(-arrowSize, arrowSize)); + painter.restore(); +} + +QDebug operator<<(QDebug debug, const QTouchDevice *d) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + debug << "QTouchDevice(" << d->name() << ','; + switch (d->type()) { + case QTouchDevice::TouchScreen: + debug << "TouchScreen"; + break; + case QTouchDevice::TouchPad: + debug << "TouchPad"; + break; + } + debug << ", capabilities=" << d->capabilities() + << ", maximumTouchPoints=" << d->maximumTouchPoints() << ')'; + return debug; +} + +// Hierarchy of classes containing gesture parameters and drawing functionality. +class Gesture { + Q_DISABLE_COPY(Gesture) +public: + static Gesture *fromQGesture(const QWidget *w, const QGesture *source); + virtual ~Gesture() {} + + virtual void draw(const QRectF &rect, QPainter &painter) const = 0; + +protected: + explicit Gesture(const QWidget *w, const QGesture *source) : m_type(source->gestureType()) + , m_hotSpot(w->mapFromGlobal(source->hotSpot().toPoint())) + , m_hasHotSpot(source->hasHotSpot()) {} + + QPointF drawHotSpot(const QRectF &rect, QPainter &painter) const + { + const QPointF h = m_hasHotSpot ? m_hotSpot : rect.center(); + painter.drawEllipse(h, 15, 15); + return h; + } + +private: + Qt::GestureType m_type; + QPointF m_hotSpot; + bool m_hasHotSpot; +}; + +class PanGesture : public Gesture { +public: + explicit PanGesture(const QWidget *w, const QPanGesture *source) : Gesture(w, source) + , m_offset(source->offset()) {} + + void draw(const QRectF &rect, QPainter &painter) const Q_DECL_OVERRIDE + { + const QPointF hotSpot = drawHotSpot(rect, painter); + painter.drawLine(hotSpot, hotSpot + m_offset); + } + +private: + QPointF m_offset; +}; + +class SwipeGesture : public Gesture { +public: + explicit SwipeGesture(const QWidget *w, const QSwipeGesture *source) : Gesture(w, source) + , m_horizontal(source->horizontalDirection()), m_vertical(source->verticalDirection()) + , m_angle(source->swipeAngle()) {} + + void draw(const QRectF &rect, QPainter &painter) const Q_DECL_OVERRIDE; + +private: + QSwipeGesture::SwipeDirection m_horizontal; + QSwipeGesture::SwipeDirection m_vertical; + qreal m_angle; +}; + +static qreal swipeDirectionAngle(QSwipeGesture::SwipeDirection d) +{ + switch (d) { + case QSwipeGesture::NoDirection: + case QSwipeGesture::Right: + break; + case QSwipeGesture::Left: + return 180; + case QSwipeGesture::Up: + return 90; + case QSwipeGesture::Down: + return 270; + } + return 0; +} + +void SwipeGesture::draw(const QRectF &rect, QPainter &painter) const +{ + enum { arrowLength = 50, arrowHeadSize = 10 }; + const QPointF hotSpot = drawHotSpot(rect, painter); + drawArrow(hotSpot, arrowLength, swipeDirectionAngle(m_horizontal), Qt::red, arrowHeadSize, painter); + drawArrow(hotSpot, arrowLength, swipeDirectionAngle(m_vertical), Qt::green, arrowHeadSize, painter); + drawArrow(hotSpot, arrowLength, m_angle, Qt::blue, arrowHeadSize, painter); +} + +Gesture *Gesture::fromQGesture(const QWidget *w, const QGesture *source) +{ + Gesture *result = Q_NULLPTR; + switch (source->gestureType()) { + case Qt::TapGesture: + case Qt::TapAndHoldGesture: + case Qt::PanGesture: + result = new PanGesture(w, static_cast<const QPanGesture *>(source)); + break; + case Qt::PinchGesture: + case Qt::CustomGesture: + case Qt::LastGestureType: + break; + case Qt::SwipeGesture: + result = new SwipeGesture(w, static_cast<const QSwipeGesture *>(source)); + break; + } + return result; +} + +typedef QSharedPointer<Gesture> GesturePtr; +typedef QVector<GesturePtr> GesturePtrs; + typedef QVector<QEvent::Type> EventTypeVector; class EventFilter : public QObject { @@ -68,33 +234,189 @@ bool EventFilter::eventFilter(QObject *o, QEvent *e) static int n = 0; if (m_types.contains(e->type())) { QString message; - QDebug(&message) << '#' << n++ << ' ' << o->objectName() << ' ' << e; + QDebug debug(&message); + debug << '#' << n++ << ' ' << o->objectName() << ' '; + switch (e->type()) { + case QEvent::Gesture: + case QEvent::GestureOverride: + debug << static_cast<const QGestureEvent *>(e); // Special operator + break; + default: + debug << e; + break; + } emit eventReceived(message); } return false; } +enum PointType { + TouchPoint, + MousePress, + MouseRelease +}; + +struct Point +{ + Point(const QPointF &p = QPoint(), PointType t = TouchPoint, + Qt::MouseEventSource s = Qt::MouseEventNotSynthesized) : pos(p), type(t), source(s) {} + + QColor color() const; + + QPointF pos; + PointType type; + Qt::MouseEventSource source; +}; + +QColor Point::color() const +{ + Qt::GlobalColor globalColor = Qt::black; + if (type != TouchPoint) { + switch (source) { + case Qt::MouseEventSynthesizedBySystem: + globalColor = Qt::red; + break; + case Qt::MouseEventSynthesizedByQt: + globalColor = Qt::blue; + break; + case Qt::MouseEventNotSynthesized: + break; + } + } + const QColor result(globalColor); + return type == MousePress ? result.lighter() : result; +} + class TouchTestWidget : public QWidget { + Q_OBJECT + Q_PROPERTY(bool drawPoints READ drawPoints WRITE setDrawPoints) public: - explicit TouchTestWidget(QWidget *parent = 0) : QWidget(parent) + explicit TouchTestWidget(QWidget *parent = 0) : QWidget(parent), m_drawPoints(true) { setAttribute(Qt::WA_AcceptTouchEvents); + foreach (Qt::GestureType t, optGestures) + grabGesture(t); } - bool event(QEvent *event) Q_DECL_OVERRIDE - { - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: + bool drawPoints() const { return m_drawPoints; } + +public slots: + void clearPoints(); + void setDrawPoints(bool drawPoints); + +signals: + void logMessage(const QString &); + +protected: + bool event(QEvent *event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + +private: + void handleGestureEvent(QGestureEvent *gestureEvent); + + QVector<Point> m_points; + GesturePtrs m_gestures; + bool m_drawPoints; +}; + +void TouchTestWidget::clearPoints() +{ + if (!m_points.isEmpty() || !m_gestures.isEmpty()) { + m_points.clear(); + m_gestures.clear(); + update(); + } +} + +void TouchTestWidget::setDrawPoints(bool drawPoints) +{ + if (m_drawPoints != drawPoints) { + clearPoints(); + m_drawPoints = drawPoints; + } +} + +bool TouchTestWidget::event(QEvent *event) +{ + const QEvent::Type type = event->type(); + switch (type) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + if (m_drawPoints) { + const QMouseEvent *me = static_cast<const QMouseEvent *>(event); + m_points.append(Point(me->localPos(), + type == QEvent::MouseButtonPress ? MousePress : MouseRelease, + me->source())); + update(); + } + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + if (m_drawPoints) { + foreach (const QTouchEvent::TouchPoint &p, static_cast<const QTouchEvent *>(event)->touchPoints()) + m_points.append(Point(p.pos(), TouchPoint)); + update(); + } + case QEvent::TouchEnd: + if (optIgnoreTouch) + event->ignore(); + else event->accept(); - return true; - default: - break; + return true; + case QEvent::Gesture: + handleGestureEvent(static_cast<QGestureEvent *>(event)); + break; + default: + break; + } + return QWidget::event(event); +} + +void TouchTestWidget::handleGestureEvent(QGestureEvent *gestureEvent) +{ + foreach (QGesture *gesture, gestureEvent->gestures()) { + if (optGestures.contains(gesture->gestureType())) { + switch (gesture->state()) { + case Qt::NoGesture: + break; + case Qt::GestureStarted: + case Qt::GestureUpdated: + gestureEvent->accept(gesture); + break; + case Qt::GestureFinished: + gestureEvent->accept(gesture); + if (Gesture *g = Gesture::fromQGesture(this, gesture)) { + m_gestures.append(GesturePtr(g)); + update(); + } + break; + case Qt::GestureCanceled: + emit logMessage(QLatin1String("=== Qt::GestureCanceled ===")); + break; + } } - return QWidget::event(event); } -}; +} + +void TouchTestWidget::paintEvent(QPaintEvent *) +{ + // Draw touch points as dots, mouse press as light filled circles, mouse release as circles. + QPainter painter(this); + const QRectF geom = QRectF(QPointF(0, 0), QSizeF(size())); + painter.fillRect(geom, Qt::white); + painter.drawRect(QRectF(geom.topLeft(), geom.bottomRight() - QPointF(1, 1))); + foreach (const Point &point, m_points) { + if (geom.contains(point.pos)) { + const qreal radius = point.type == TouchPoint ? 1 : 4; + if (point.type == MouseRelease) { + drawCircle(point.pos, radius, point.color(), painter); + } else + fillCircle(point.pos, radius, point.color(), painter); + } + } + foreach (const GesturePtr &gp, m_gestures) + gp->draw(geom, painter); +} class MainWindow : public QMainWindow { @@ -108,7 +430,7 @@ public slots: void dumpTouchDevices(); private: - QWidget *m_touchWidget; + TouchTestWidget *m_touchWidget; QPlainTextEdit *m_logTextEdit; }; @@ -119,23 +441,38 @@ MainWindow::MainWindow() setWindowTitle(QStringLiteral("Touch Event Tester ") + QT_VERSION_STR); setObjectName("MainWin"); + QToolBar *toolBar = new QToolBar(this); + addToolBar(Qt::TopToolBarArea, toolBar); QMenu *fileMenu = menuBar()->addMenu("File"); - QAction *da = fileMenu->addAction(QStringLiteral("Dump devices")); - da->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); - connect(da, SIGNAL(triggered()), this, SLOT(dumpTouchDevices())); - QAction *qa = fileMenu->addAction(QStringLiteral("Quit")); - qa->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); - connect(qa, SIGNAL(triggered()), this, SLOT(close())); + QAction *dumpDeviceAction = fileMenu->addAction(QStringLiteral("Dump devices")); + dumpDeviceAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); + connect(dumpDeviceAction, SIGNAL(triggered()), this, SLOT(dumpTouchDevices())); + toolBar->addAction(dumpDeviceAction); + QAction *clearLogAction = fileMenu->addAction(QStringLiteral("Clear Log")); + clearLogAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + connect(clearLogAction, SIGNAL(triggered()), m_logTextEdit, SLOT(clear())); + toolBar->addAction(clearLogAction); + QAction *toggleDrawPointAction = fileMenu->addAction(QStringLiteral("Draw Points")); + toggleDrawPointAction->setCheckable(true); + toggleDrawPointAction->setChecked(m_touchWidget->drawPoints()); + connect(toggleDrawPointAction, SIGNAL(toggled(bool)), m_touchWidget, SLOT(setDrawPoints(bool))); + toolBar->addAction(toggleDrawPointAction); + QAction *clearPointAction = fileMenu->addAction(QStringLiteral("Clear Points")); + clearPointAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P)); + connect(clearPointAction, SIGNAL(triggered()), m_touchWidget, SLOT(clearPoints())); + toolBar->addAction(clearPointAction); + QAction *quitAction = fileMenu->addAction(QStringLiteral("Quit")); + quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); + connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); + toolBar->addAction(quitAction); - QSplitter *mainSplitter = new QSplitter(Qt::Vertical); + QSplitter *mainSplitter = new QSplitter(Qt::Vertical, this); m_touchWidget->setObjectName(QStringLiteral("TouchWidget")); - const QSize screenSize = QGuiApplication::primaryScreen()->availableGeometry().size(); - m_touchWidget->setMinimumSize(screenSize / 2); mainSplitter->addWidget(m_touchWidget); + connect(m_touchWidget, &TouchTestWidget::logMessage, this, &MainWindow::appendToLog); m_logTextEdit->setObjectName(QStringLiteral("LogTextEdit")); - m_logTextEdit->setMinimumHeight(screenSize.height() / 4); mainSplitter->addWidget(m_logTextEdit); setCentralWidget(mainSplitter); @@ -165,19 +502,57 @@ int main(int argc, char *argv[]) const QCommandLineOption globalFilterOption(QStringLiteral("global"), QStringLiteral("Global event filter")); parser.addOption(globalFilterOption); + + const QCommandLineOption ignoreTouchOption(QStringLiteral("ignore"), + QStringLiteral("Ignore touch events (for testing mouse emulation).")); + parser.addOption(ignoreTouchOption); + const QCommandLineOption noTouchLogOption(QStringLiteral("notouchlog"), + QStringLiteral("Do not log touch events (for testing gestures).")); + parser.addOption(noTouchLogOption); + const QCommandLineOption noMouseLogOption(QStringLiteral("nomouselog"), + QStringLiteral("Do not log mouse events (for testing gestures).")); + parser.addOption(noMouseLogOption); + + const QCommandLineOption tapGestureOption(QStringLiteral("tap"), QStringLiteral("Grab tap gesture.")); + parser.addOption(tapGestureOption); + const QCommandLineOption tapAndHoldGestureOption(QStringLiteral("tap-and-hold"), + QStringLiteral("Grab tap-and-hold gesture.")); + parser.addOption(tapAndHoldGestureOption); + const QCommandLineOption panGestureOption(QStringLiteral("pan"), QStringLiteral("Grab pan gesture.")); + parser.addOption(panGestureOption); + const QCommandLineOption pinchGestureOption(QStringLiteral("pinch"), QStringLiteral("Grab pinch gesture.")); + parser.addOption(pinchGestureOption); + const QCommandLineOption swipeGestureOption(QStringLiteral("swipe"), QStringLiteral("Grab swipe gesture.")); + parser.addOption(swipeGestureOption); parser.process(QApplication::arguments()); + optIgnoreTouch = parser.isSet(ignoreTouchOption); + if (parser.isSet(tapGestureOption)) + optGestures.append(Qt::TapGesture); + if (parser.isSet(tapAndHoldGestureOption)) + optGestures.append(Qt::TapAndHoldGesture); + if (parser.isSet(panGestureOption)) + optGestures.append(Qt::PanGesture); + if (parser.isSet(pinchGestureOption)) + optGestures.append(Qt::PinchGesture); + if (parser.isSet(swipeGestureOption)) + optGestures.append(Qt::SwipeGesture); MainWindow w; + const QSize screenSize = QGuiApplication::primaryScreen()->availableGeometry().size(); + w.resize(screenSize / 2); + const QSize sizeDiff = screenSize - w.size(); + w.move(sizeDiff.width() / 2, sizeDiff.height() / 2); w.show(); - const QSize pos = QGuiApplication::primaryScreen()->availableGeometry().size() - w.size(); - w.move(pos.width() / 2, pos.height() / 2); EventTypeVector eventTypes; - eventTypes << QEvent::MouseButtonPress << QEvent::MouseButtonRelease - << QEvent::MouseButtonDblClick - << QEvent::TouchBegin << QEvent::TouchUpdate << QEvent::TouchEnd; + if (!parser.isSet(noMouseLogOption)) + eventTypes << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick; if (parser.isSet(mouseMoveOption)) eventTypes << QEvent::MouseMove; + if (!parser.isSet(noTouchLogOption)) + eventTypes << QEvent::TouchBegin << QEvent::TouchUpdate << QEvent::TouchEnd; + if (!optGestures.isEmpty()) + eventTypes << QEvent::Gesture << QEvent::GestureOverride; QObject *filterTarget = parser.isSet(globalFilterOption) ? static_cast<QObject *>(&a) : static_cast<QObject *>(w.touchWidget()); |