diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2020-08-12 13:21:42 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2020-08-17 15:08:39 +0200 |
commit | c54a5b83804c00474d141b485b752a7c54169ebf (patch) | |
tree | 9016e1579b219072547a4d6892f500929e01e3c8 | |
parent | c4366ff0183a9a4a5c6eff0312b713e9c5eb97ea (diff) |
Introduce QWidget::setScreen
Follows the QWindow semantics, and is a replacement for creating
a QWidget with a QDesktopScreenWidget as the parent.
We can now remove much of the special handling of QDesktopWidget and
the Qt::Desktop window type, and get rid of QDesktopScreenWidget.
Add a manual test that allows local testing. Our CI environments
only have a single screen, and no multi-head display server setup
which is the primary case where QWidget::setScreen is interesting.
For the more common case of a virtual desktop, QWidget::setScreen
has no real impact (just as QWindow::setScreen doesn't).
Change-Id: Id0099e069d316741bacd8c795c396ccad37be297
Fixes: QTBUG-85483
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
-rw-r--r-- | src/corelib/global/qnamespace.qdoc | 2 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible.cpp | 2 | ||||
-rw-r--r-- | src/widgets/accessible/qaccessiblewidgetfactory.cpp | 2 | ||||
-rw-r--r-- | src/widgets/kernel/qdesktopwidget.cpp | 32 | ||||
-rw-r--r-- | src/widgets/kernel/qdesktopwidget_p.h | 12 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 44 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.h | 3 | ||||
-rw-r--r-- | src/widgets/widgets/qmenu.cpp | 2 | ||||
-rw-r--r-- | tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp | 24 | ||||
-rw-r--r-- | tests/manual/widgets/kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/manual/widgets/kernel/kernel.pro | 2 | ||||
-rw-r--r-- | tests/manual/widgets/kernel/setscreen/CMakeLists.txt | 18 | ||||
-rw-r--r-- | tests/manual/widgets/kernel/setscreen/main.cpp | 156 | ||||
-rw-r--r-- | tests/manual/widgets/kernel/setscreen/setscreen.pro | 3 |
14 files changed, 239 insertions, 64 deletions
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index f63b58f515..ad79e279be 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -2173,7 +2173,7 @@ \value SplashScreen Indicates that the window is a splash screen. This is the default type for QSplashScreen. - \value Desktop Indicates that this widget is the desktop. This + \omitvalue Desktop Indicates that this widget is the desktop. This is the type for QDesktopWidget. \value SubWindow Indicates that this widget is a sub-window, such diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index 22d94fad79..7fca4fb0f7 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -707,7 +707,7 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) QAccessiblePlugin *factory = qAccessiblePlugins()->value(cn); if (factory) { QAccessibleInterface *result = factory->create(cn, object); - if (result) { // Need this condition because of QDesktopScreenWidget + if (result) { QAccessibleCache::instance()->insert(object, result); Q_ASSERT(QAccessibleCache::instance()->containsObject(object)); } diff --git a/src/widgets/accessible/qaccessiblewidgetfactory.cpp b/src/widgets/accessible/qaccessiblewidgetfactory.cpp index d59da86076..4c6a6fdecb 100644 --- a/src/widgets/accessible/qaccessiblewidgetfactory.cpp +++ b/src/widgets/accessible/qaccessiblewidgetfactory.cpp @@ -222,8 +222,6 @@ QAccessibleInterface *qAccessibleFactory(const QString &classname, QObject *obje iface = new QAccessibleDockWidget(widget); #endif - } else if (classname == QLatin1String("QDesktopScreenWidget")) { - iface = nullptr; } else if (classname == QLatin1String("QWidget")) { iface = new QAccessibleWidget(widget); } else if (classname == QLatin1String("QWindowContainer")) { diff --git a/src/widgets/kernel/qdesktopwidget.cpp b/src/widgets/kernel/qdesktopwidget.cpp index 797980c7c5..db8c33c498 100644 --- a/src/widgets/kernel/qdesktopwidget.cpp +++ b/src/widgets/kernel/qdesktopwidget.cpp @@ -48,26 +48,6 @@ QT_BEGIN_NAMESPACE -QDesktopScreenWidget::QDesktopScreenWidget(QScreen *screen, const QRect &geometry) - : QWidget(nullptr, Qt::Desktop) -{ - setVisible(false); - if (QWindow *winHandle = windowHandle()) - winHandle->setScreen(screen); - setGeometry(geometry); -} - -QScreen *QDesktopScreenWidget::screen() const -{ - const QDesktopWidgetPrivate *desktopWidgetP - = static_cast<const QDesktopWidgetPrivate *>(qt_widget_private(QApplication::desktop())); - for (auto it : qAsConst(desktopWidgetP->screenWidgets)) { - if (it.second == this) - return it.first; - } - return nullptr; -} - QDesktopWidgetPrivate::~QDesktopWidgetPrivate() { qDeleteAll(screenWidgets.values()); @@ -81,15 +61,19 @@ void QDesktopWidgetPrivate::updateScreens() // Re-build our screens list. This is the easiest way to later compute which signals to emit. // Create new screen widgets as necessary. // Furthermore, we note which screens have changed, and compute the overall virtual geometry. - QFlatMap<QScreen*, QDesktopScreenWidget*> newScreenWidgets; + QFlatMap<QScreen*, QWidget*> newScreenWidgets; QRegion virtualGeometry; for (QScreen *screen : screenList) { const QRect screenGeometry = screen->geometry(); - QDesktopScreenWidget *screenWidget = screenWidgets.value(screen); + QWidget *screenWidget = screenWidgets.value(screen); if (!screenWidget) { // a new screen, create a widget and connect the signals. - screenWidget = new QDesktopScreenWidget(screen, screenGeometry); + screenWidget = new QWidget(nullptr, Qt::Desktop); + screenWidget->setVisible(false); + screenWidget->setScreen(screen); + screenWidget->setGeometry(screenGeometry); + screenWidget->setObjectName(QLatin1String("qt_desktop_widget_%1").arg(screen->name())); QObjectPrivate::connect(screen, &QScreen::geometryChanged, this, &QDesktopWidgetPrivate::updateScreens, Qt::QueuedConnection); QObjectPrivate::connect(screen, &QObject::destroyed, @@ -105,7 +89,7 @@ void QDesktopWidgetPrivate::updateScreens() Q_ASSERT(screenWidgets.size() == screenList.length()); q->setGeometry(virtualGeometry.boundingRect()); - // Delete the QDesktopScreenWidget that are not used any more. + // Delete the screen widgets that are not used any more. for (auto it : qAsConst(newScreenWidgets)) { if (!screenWidgets.contains(it.first)) delete it.second; diff --git a/src/widgets/kernel/qdesktopwidget_p.h b/src/widgets/kernel/qdesktopwidget_p.h index f0664794bc..c17036b9d0 100644 --- a/src/widgets/kernel/qdesktopwidget_p.h +++ b/src/widgets/kernel/qdesktopwidget_p.h @@ -77,26 +77,18 @@ private: friend class QApplicationPrivate; }; -class QDesktopScreenWidget : public QWidget { - Q_OBJECT -public: - explicit QDesktopScreenWidget(QScreen *, const QRect &geometry); - - QScreen *screen() const; -}; - class QDesktopWidgetPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QDesktopWidget) public: ~QDesktopWidgetPrivate(); void updateScreens(); - QDesktopScreenWidget *widgetForScreen(QScreen *qScreen) const + QWidget *widgetForScreen(QScreen *qScreen) const { return screenWidgets.value(qScreen); } - QFlatMap<QScreen*, QDesktopScreenWidget*> screenWidgets; + QFlatMap<QScreen*, QWidget*> screenWidgets; }; QT_END_NAMESPACE diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 2bfb4c3096..38f51e270e 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -42,7 +42,6 @@ #include "qapplication_p.h" #include "qbrush.h" #include "qcursor.h" -#include "qdesktopwidget_p.h" #include "qevent.h" #include "qlayout.h" #if QT_CONFIG(menu) @@ -990,13 +989,6 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (allWidgets) allWidgets->insert(q); - QScreen *targetScreen = nullptr; - if (parentWidget && parentWidget->windowType() == Qt::Desktop) { - const QDesktopScreenWidget *sw = qobject_cast<const QDesktopScreenWidget *>(parentWidget); - targetScreen = sw ? sw->screen() : nullptr; - parentWidget = nullptr; - } - q->data = &data; #if QT_CONFIG(thread) @@ -1006,12 +998,6 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) } #endif - if (targetScreen) { - topData()->initialScreen = targetScreen; - if (QWindow *window = q->windowHandle()) - window->setScreen(targetScreen); - } - data.fstrut_dirty = true; data.winid = 0; @@ -2424,8 +2410,7 @@ bool QWidgetPrivate::setScreen(QScreen *screen) return false; const QScreen *currentScreen = windowHandle() ? windowHandle()->screen() : nullptr; if (currentScreen != screen) { - if (!windowHandle()) // Try to create a window handle if not created. - createWinId(); + topData()->initialScreen = screen; if (windowHandle()) windowHandle()->setScreen(screen); return true; @@ -2514,6 +2499,24 @@ QScreen *QWidget::screen() const return QGuiApplication::primaryScreen(); } +/*! + Sets the screen on which the widget should be shown to \a screen. + + Setting the screen only makes sense for windows. If necessary, the widget's + window will get recreated on \a screen. + + \note If the screen is part of a virtual desktop of multiple screens, + the window will not move automatically to \a newScreen. To place the + window relative to the screen, use the screen's topLeft() position. + + \sa QWindow::setScreen +*/ +void QWidget::setScreen(QScreen *screen) +{ + Q_D(QWidget); + d->setScreen(screen); +} + #ifndef QT_NO_STYLE_STYLESHEET /*! @@ -10508,8 +10511,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) if (newparent && newparent->windowType() == Qt::Desktop) { // make sure the widget is created on the same screen as the // programmer specified desktop widget - const QDesktopScreenWidget *sw = qobject_cast<const QDesktopScreenWidget *>(newparent); - targetScreen = sw ? sw->screen() : nullptr; + targetScreen = newparent->screen(); newparent = nullptr; } @@ -10539,10 +10541,8 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) if (!newparent) { f |= Qt::Window; - if (!targetScreen) { - if (parent) - targetScreen = q->parentWidget()->window()->screen(); - } + if (parent) + targetScreen = q->parentWidget()->window()->screen(); } bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h index 2bfff96252..6f4c709c10 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -598,11 +598,10 @@ public: QWindow *windowHandle() const; QScreen *screen() const; + void setScreen(QScreen *); static QWidget *createWindowContainer(QWindow *window, QWidget *parent=nullptr, Qt::WindowFlags flags=Qt::WindowFlags()); - friend class QDesktopScreenWidget; - Q_SIGNALS: void windowTitleChanged(const QString &title); void windowIconChanged(const QIcon &icon); diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 74420ecb2c..9528be17b7 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -2377,7 +2377,7 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po updateLayoutDirection(); // Ensure that we get correct sizeHints by placing this window on the correct screen. - // However if the QMenu was constructed with a QDesktopScreenWidget as its parent, + // However if the QMenu was constructed with a Qt::Desktop widget as its parent, // then initialScreenIndex was set, so we should respect that for the lifetime of this menu. // However if eventLoop exists, then exec() already did this by calling createWinId(); so leave it alone. (QTBUG-76162) if (!eventLoop) { diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 6e304f2152..ae91685697 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -196,6 +196,7 @@ private slots: void activation(); #endif void reparent(); + void setScreen(); void windowState(); void showMaximized(); void showFullScreen(); @@ -2943,6 +2944,29 @@ void tst_QWidget::reparent() QTRY_COMPARE(childTLW.pos(), tlwPos); } +void tst_QWidget::setScreen() +{ + const auto screens = QApplication::screens(); + if (screens.count() < 2) + QSKIP("This test tests nothing on a machine with a single screen."); + + QScreen *screen0 = screens.at(0); + QScreen *screen1 = screens.at(1); + + QWidget window; + window.setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint); + window.setScreen(screen0); + QCOMPARE(window.screen(), screen0); + window.setScreen(screen1); + QCOMPARE(window.screen(), screen1); + + // calling setScreen on a widget that is not a window does nothing + QWidget child(&window); + const QScreen *childScreen = child.screen(); + child.setScreen(childScreen == screen0 ? screen1 : screen0); + QCOMPARE(child.screen(), childScreen); +} + // Qt/Embedded does it differently. void tst_QWidget::icon() { diff --git a/tests/manual/widgets/kernel/CMakeLists.txt b/tests/manual/widgets/kernel/CMakeLists.txt index 9be003391d..b4443fa3e2 100644 --- a/tests/manual/widgets/kernel/CMakeLists.txt +++ b/tests/manual/widgets/kernel/CMakeLists.txt @@ -3,3 +3,4 @@ # add_subdirectory(qtooltip) # special case broken in dev add_subdirectory(sizeonhide) add_subdirectory(layoutreplace) +add_subdirectory(setscreen) diff --git a/tests/manual/widgets/kernel/kernel.pro b/tests/manual/widgets/kernel/kernel.pro index 5dbd25f8bd..236c9ff9d6 100644 --- a/tests/manual/widgets/kernel/kernel.pro +++ b/tests/manual/widgets/kernel/kernel.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS = qtooltip sizeonhide layoutreplace +SUBDIRS = qtooltip sizeonhide layoutreplace setscreen diff --git a/tests/manual/widgets/kernel/setscreen/CMakeLists.txt b/tests/manual/widgets/kernel/setscreen/CMakeLists.txt new file mode 100644 index 0000000000..cf31341d7c --- /dev/null +++ b/tests/manual/widgets/kernel/setscreen/CMakeLists.txt @@ -0,0 +1,18 @@ +# Generated from setscreen.pro. + +##################################################################### +## setscreen Binary: +##################################################################### + +qt_add_manual_test(setscreen + GUI + SOURCES + main.cpp + PUBLIC_LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::Widgets +) + +#### Keys ignored in scope 1:.:.:setscreen.pro:<TRUE>: +# TEMPLATE = "app" diff --git a/tests/manual/widgets/kernel/setscreen/main.cpp b/tests/manual/widgets/kernel/setscreen/main.cpp new file mode 100644 index 0000000000..70ec067d3d --- /dev/null +++ b/tests/manual/widgets/kernel/setscreen/main.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWidgets> + +class ScreenWidget : public QWidget +{ +public: + ScreenWidget(QWidget *parent) + : QWidget(parent, Qt::Window) + { + textEdit = new QTextEdit; + textEdit->setReadOnly(true); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(textEdit); + setLayout(layout); + } + + void updateText() + { + QString text = "<html><body>"; + text += QString("<p>Screen: %1\n</p>").arg(screen()->name()); + text += QString("<p>DPR: %1\n</p>").arg(screen()->devicePixelRatio()); + text += QString("</body></html>"); + + textEdit->setText(text); + } + +private: + QTextEdit *textEdit; +}; + +class Controller : public QDialog +{ + Q_OBJECT + +public: + Controller() + { + QPushButton *screenButton = new QPushButton; + screenButton->setText("Show on Screen"); + screenButton->setEnabled(false); + connect(screenButton, &QAbstractButton::clicked, this, &Controller::setScreen); + + QPushButton *desktopButton = new QPushButton; + desktopButton->setText("Show on Desktop"); + desktopButton->setEnabled(false); + connect(desktopButton, &QAbstractButton::clicked, this, &Controller::setDesktop); + + QPushButton *exitButton = new QPushButton; + exitButton->setText("E&xit"); + connect(exitButton, &QAbstractButton::clicked, QApplication::instance(), &QCoreApplication::quit); + + QHBoxLayout *actionLayout = new QHBoxLayout; + actionLayout->addWidget(screenButton); + actionLayout->addWidget(desktopButton); + actionLayout->addWidget(exitButton); + + QGroupBox *radioGroup = new QGroupBox; + radioGroup->setTitle(QLatin1String("Select target screen")); + + QVBoxLayout *groupLayout = new QVBoxLayout; + const auto screens = QGuiApplication::screens(); + int count = 0; + for (const auto &screen : screens) { + QRadioButton *choice = new QRadioButton; + choice->setText(QString(QLatin1String("%1: %2")).arg(count).arg(screen->name())); + connect(choice, &QAbstractButton::toggled, this, [=](bool on){ + if (on) + targetScreen = count; + screenButton->setEnabled(targetScreen != -1); + desktopButton->setEnabled(targetScreen != -1); + }); + groupLayout->addWidget(choice); + ++count; + } + radioGroup->setLayout(groupLayout); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(radioGroup); + layout->addLayout(actionLayout); + setLayout(layout); + } + +private slots: + void setScreen() + { + QScreen *screen = QGuiApplication::screens().at(targetScreen); + if (!widget) { + widget = new ScreenWidget(this); + widget->setAttribute(Qt::WA_DeleteOnClose); + widget->setWindowTitle("Normal Window"); + } + widget->setScreen(screen); + widget->show(); + widget->updateText(); + } + + void setDesktop() + { + QScreen *screen = QGuiApplication::screens().at(targetScreen); + QWidget *desktop = QApplication::desktop(screen); + if (!desktopChild) { + desktopChild = new ScreenWidget(desktop); + desktopChild->setAttribute(Qt::WA_DeleteOnClose); + desktopChild->setWindowTitle("Child of a Desktop"); + } else { + desktopChild->setParent(desktop); + } + desktopChild->show(); + desktopChild->updateText(); + } + +private: + QPointer<ScreenWidget> widget = nullptr; + QPointer<ScreenWidget> desktopChild = nullptr; + int targetScreen = -1; +}; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + Controller controller; + controller.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/kernel/setscreen/setscreen.pro b/tests/manual/widgets/kernel/setscreen/setscreen.pro new file mode 100644 index 0000000000..f06006ea3a --- /dev/null +++ b/tests/manual/widgets/kernel/setscreen/setscreen.pro @@ -0,0 +1,3 @@ +TEMPLATE = app +SOURCES = main.cpp +QT += widgets |