From 6405b730dc6cebef1734d612114525c4b4a20260 Mon Sep 17 00:00:00 2001 From: Wayne Arnold Date: Tue, 22 Aug 2017 13:05:50 -0400 Subject: [PATCH 1/6] Fixes QTBUG-56277 --- src/widgets/kernel/qwidget.cpp | 14 +++++--- .../widgets/kernel/qwidget/tst_qwidget.cpp | 2 ++ .../qwidget_window/tst_qwidget_window.cpp | 34 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index a6c844fcffa..fb8e37da2dc 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -7191,7 +7191,8 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) QRect r(x, y, w, h); bool isResize = olds != r.size(); - isMove = oldp != r.topLeft(); //### why do we have isMove as a parameter? + if (!isMove) + isMove = oldp != r.topLeft(); // We only care about stuff that changes the geometry, or may @@ -7225,12 +7226,17 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (q->isVisible()) { if (!q->testAttribute(Qt::WA_DontShowOnScreen) && !q->testAttribute(Qt::WA_OutsideWSRange)) { - if (q->windowHandle()) { + if (QWindow *win = q->windowHandle()) { if (q->isWindow()) { - q->windowHandle()->setGeometry(q->geometry()); + if (isResize && !isMove) + win->resize(w, h); + else if (isMove && !isResize) + win->setPosition(x, y); + else + win->setGeometry(q->geometry()); } else { QPoint posInNativeParent = q->mapTo(q->nativeParentWidget(),QPoint()); - q->windowHandle()->setGeometry(QRect(posInNativeParent,r.size())); + win->setGeometry(QRect(posInNativeParent,r.size())); } if (needsShow) diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 22963a03fcb..b1924a59512 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -4826,6 +4826,8 @@ void tst_QWidget::windowMoveResize_data() void tst_QWidget::windowMoveResize() { + qDebug() << "platform" << m_platform; + qDebug() << "XDG_CURRENT_DESKTOP" << qgetenv("XDG_CURRENT_DESKTOP"); if (m_platform == QStringLiteral("xcb")) QSKIP("X11: Skip this test due to Window manager positioning issues."); if (m_platform == QStringLiteral("wayland")) diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index 3a4b563a6c4..a8bb6d71bde 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -104,6 +104,8 @@ private slots: void tst_eventfilter_on_toplevel(); void QTBUG_50561_QCocoaBackingStore_paintDevice_crash(); + + void tst_resize_on_showEvent_QTBUG56277(); }; void tst_QWidget_window::initTestCase() @@ -864,5 +866,37 @@ void tst_QWidget_window::QTBUG_50561_QCocoaBackingStore_paintDevice_crash() w.close(); } +class ResizedOnShowEventWidget : public QWidget +{ +public: + void showEvent(QShowEvent *) override + { + const auto *primaryScreen = QApplication::primaryScreen(); + auto newSize = primaryScreen->availableGeometry().size() / 4; + if (newSize == geometry().size()) + newSize -= QSize(10, 10); + resize(newSize); + } +}; + +void tst_QWidget_window::tst_resize_on_showEvent_QTBUG56277() +{ + if (!QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive) + && qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity") + QSKIP("Unity shows new windows in the top-left corner, so the test is irrelevant."); + + ResizedOnShowEventWidget w; + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + const auto *screen = w.windowHandle()->screen(); + QStyleOption opt; + opt.initFrom(&w); + const auto geometry = w.geometry(); + const int frameHeight = geometry.top() - w.frameGeometry().top(); + const int topmostY = screen->availableGeometry().top() + frameHeight; + QVERIFY(geometry.top() > topmostY); + QVERIFY(geometry.left() > screen->availableGeometry().left()); +} + QTEST_MAIN(tst_QWidget_window) #include "tst_qwidget_window.moc" From b1fbac29d11c438acff71c7e679a73c5e79174b0 Mon Sep 17 00:00:00 2001 From: Wayne Arnold Date: Tue, 22 Aug 2017 13:07:15 -0400 Subject: [PATCH 2/6] Fixes MAYA-83867 File dialog slows down with network drive shortcuts See https://codereview.qt-project.org/#/c/178144/ --- src/corelib/io/qfileinfo.cpp | 20 ++++++++++++++++++++ src/corelib/io/qfileinfo.h | 2 ++ src/corelib/io/qfilesystemengine.cpp | 4 ++++ src/corelib/io/qfilesystemengine_win.cpp | 2 ++ src/widgets/dialogs/qfileinfogatherer.cpp | 10 +++++++--- src/widgets/dialogs/qfileinfogatherer_p.h | 4 ++++ 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 4536b31c35b..03cacf9a72c 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -306,6 +306,8 @@ bool QFileInfoPrivate::isConnected() const { refreshes the file information: refresh(). If you want to switch off a QFileInfo's caching and force it to access the file system every time you request information from it call setCaching(false). + For Windows, getAllMetadata() can be used to improve performance + of searching file information. \sa QDir, QFile */ @@ -1410,6 +1412,24 @@ void QFileInfo::setCaching(bool enable) d->cache_enabled = enable; } +/*! + Returns all the supported file information internally. Currently, it is only + supported on Windows. +*/ +bool QFileInfo::getAllMetadata() const +{ + Q_D(const QFileInfo); + + if (d->fileEngine == 0) { +#if defined(Q_OS_WIN) + return QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, + QFileSystemMetaData::Permissions | + QFileSystemMetaData::LinkType); +#endif + } + return false; +} + /*! \typedef QFileInfoList \relates QFileInfo diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h index f8597bbbb21..04495d6288a 100644 --- a/src/corelib/io/qfileinfo.h +++ b/src/corelib/io/qfileinfo.h @@ -130,6 +130,8 @@ class Q_CORE_EXPORT QFileInfo bool caching() const; void setCaching(bool on); + bool getAllMetadata() const; + protected: QSharedDataPointer d_ptr; diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp index 3e7dda5b195..eaec70cd152 100644 --- a/src/corelib/io/qfilesystemengine.cpp +++ b/src/corelib/io/qfilesystemengine.cpp @@ -53,6 +53,10 @@ QString QFileSystemEngine::slowCanonicalized(const QString &path) if (path.isEmpty()) return path; + QFileInfo tmpInfo(path); + if (!tmpInfo.isSymLink()) + return path; + QFileInfo fi; const QChar slash(QLatin1Char('/')); QString tmpPath = path; diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index 0cd5a81d6f4..6c7d60379ae 100644 --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -1056,6 +1056,8 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM #ifndef Q_OS_WINRT SetErrorMode(oldmode); #endif + data.knownFlagsMask |= what; + clearWinStatData(data); return false; } } diff --git a/src/widgets/dialogs/qfileinfogatherer.cpp b/src/widgets/dialogs/qfileinfogatherer.cpp index 8c9538846c1..89cbc28199f 100644 --- a/src/widgets/dialogs/qfileinfogatherer.cpp +++ b/src/widgets/dialogs/qfileinfogatherer.cpp @@ -236,6 +236,8 @@ QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const { QExtendedInformation info(fileInfo); info.icon = m_iconProvider->icon(fileInfo); + // get metadata for all supported types at once to improve performance + fileInfo.getAllMetadata(); #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) const QFileInfoPrivate *priv = QFileInfoPrivate::get(&fileInfo); if (priv->mappedDrive) @@ -260,10 +262,12 @@ QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const #endif #ifdef Q_OS_WIN - if (m_resolveSymlinks && info.isSymLink(/* ignoreNtfsSymLinks = */ true)) { + if (fileInfo.exists() && m_resolveSymlinks && info.isSymLink(/* ignoreNtfsSymLinks = */ true)) { QFileInfo resolvedInfo(fileInfo.symLinkTarget()); - resolvedInfo = resolvedInfo.canonicalFilePath(); - if (resolvedInfo.exists()) { + resolvedInfo.getAllMetadata(); + QString canonicalFilePath = resolvedInfo.canonicalFilePath(); + if (!canonicalFilePath.isEmpty()) { + resolvedInfo = canonicalFilePath; emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName()); } } diff --git a/src/widgets/dialogs/qfileinfogatherer_p.h b/src/widgets/dialogs/qfileinfogatherer_p.h index 3481444996a..ed8295d9804 100644 --- a/src/widgets/dialogs/qfileinfogatherer_p.h +++ b/src/widgets/dialogs/qfileinfogatherer_p.h @@ -136,6 +136,10 @@ class QExtendedInformation { QString displayFileType() const; + bool getAllMetadata() const { + return mFileInfo.getAllMetadata(); + } + QString displayType; QIcon icon; From 292961f7989ca2a701a24d854cedf24fd0069baf Mon Sep 17 00:00:00 2001 From: Wayne Arnold Date: Tue, 22 Aug 2017 13:12:45 -0400 Subject: [PATCH 3/6] Fixes QTBUG-52027 --- src/widgets/widgets/qcombobox.cpp | 16 ++++- .../widgets/qcombobox/tst_qcombobox.cpp | 58 +++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 2abcd4d3c26..a8317e62997 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -190,7 +190,21 @@ void QComboBoxPrivate::_q_completerActivated(const QModelIndex &index) if (index.isValid() && q->completer()) { QAbstractProxyModel *proxy = qobject_cast(q->completer()->completionModel()); if (proxy) { - q->setCurrentIndex(proxy->mapToSource(index).row()); + const QModelIndex &completerIndex = proxy->mapToSource(index); + int row = -1; + if (completerIndex.model() == model) { + row = completerIndex.row(); + } else { + // if QCompleter uses a proxy model to host widget's one - map again + QAbstractProxyModel *completerProxy = qobject_cast(q->completer()->model()); + if (completerProxy && completerProxy->sourceModel() == model) { + row = completerProxy->mapToSource(completerIndex).row(); + } else { + QString match = q->completer()->model()->data(completerIndex).toString(); + row = q->findText(match, matchFlags()); + } + } + q->setCurrentIndex(row); emitActivated(currentIndex); } } diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index ae516639a6e..48e7bced54a 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -168,6 +168,7 @@ private slots: void task_QTBUG_39088_inputMethodHints(); void task_QTBUG_49831_scrollerNotActivated(); }; + void task_QTBUG_52027_mapCompleterIndex(); class MyAbstractItemDelegate : public QAbstractItemDelegate { @@ -3238,5 +3239,62 @@ void tst_QComboBox::task_QTBUG_49831_scrollerNotActivated() } } +void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() +{ + QStringList words; + words << "" << "foobar1" << "foobar2"; + + QStringList altWords; + altWords << "foobar2" << "hello" << "," << "world" << "" << "foobar0" << "foobar1"; + + QComboBox cbox; + setFrameless(&cbox); + cbox.setEditable(true); + cbox.setInsertPolicy(QComboBox::NoInsert); + cbox.addItems(words); + + QCompleter *completer = new QCompleter(altWords); + completer->setCaseSensitivity(Qt::CaseInsensitive); + cbox.setCompleter(completer); + + QSignalSpy spy(&cbox, SIGNAL(activated(int))); + QCOMPARE(spy.count(), 0); + cbox.move(200, 200); + cbox.show(); + QApplication::setActiveWindow(&cbox); + QVERIFY(QTest::qWaitForWindowActive(&cbox)); + + QTest::keyClicks(&cbox, "foobar2"); + QApplication::processEvents(); + QTRY_VERIFY(completer->popup()); + QTest::keyClick(completer->popup(), Qt::Key_Down); + QApplication::processEvents(); + QTest::keyClick(completer->popup(), Qt::Key_Return); + QApplication::processEvents(); + QList arguments = spy.takeLast(); + QCOMPARE(arguments.at(0).toInt(), 2); + + cbox.lineEdit()->selectAll(); + cbox.lineEdit()->del(); + + QSortFilterProxyModel* model = new QSortFilterProxyModel(); + model->setSourceModel(cbox.model()); + model->setFilterFixedString("foobar1"); + completer->setModel(model); + + QApplication::setActiveWindow(&cbox); + QVERIFY(QTest::qWaitForWindowActive(&cbox)); + + QTest::keyClicks(&cbox, "foobar1"); + QApplication::processEvents(); + QTRY_VERIFY(completer->popup()); + QTest::keyClick(completer->popup(), Qt::Key_Down); + QApplication::processEvents(); + QTest::keyClick(completer->popup(), Qt::Key_Return); + QApplication::processEvents(); + arguments = spy.takeLast(); + QCOMPARE(arguments.at(0).toInt(), 1); +} + QTEST_MAIN(tst_QComboBox) #include "tst_qcombobox.moc" From 1b81f1a11d85c78cf5e87d45abafa4a6fd14826f Mon Sep 17 00:00:00 2001 From: Wayne Arnold Date: Tue, 22 Aug 2017 13:18:06 -0400 Subject: [PATCH 4/6] Fixes QTBUG-60513 --- src/gui/kernel/qscreen.cpp | 3 +++ src/plugins/platforms/xcb/qxcbscreen.cpp | 33 ++++++++++++++++++++++++ src/plugins/platforms/xcb/qxcbscreen.h | 2 +- src/widgets/kernel/qdesktopwidget.qdoc | 2 +- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp index e87f58d7354..b2d1b6d4fb6 100644 --- a/src/gui/kernel/qscreen.cpp +++ b/src/gui/kernel/qscreen.cpp @@ -341,6 +341,9 @@ QRect QScreen::geometry() const The available geometry is the geometry excluding window manager reserved areas such as task bars and system menus. + + Note, on X11 this value currently can not always be trusted as window managers don't + expose the available geometry in a reliable way on systems with multiple screens. */ QRect QScreen::availableGeometry() const { diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index f74244e13c8..8d39f25520b 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -37,12 +37,14 @@ #include "qxcbimage.h" #include "qnamespace.h" #include "qxcbxsettings.h" +#include "qxcbintegration.h" #include #include #include +#include #include #include @@ -121,6 +123,23 @@ void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify() } } +/*! \internal + + Using _NET_WORKAREA to calculate the available desktop geometry on multi-head systems (systems + with more than one monitor) is unreliable. Different WMs have different interpretations of what + _NET_WORKAREA means with multiple attached monitors. This gets worse when monitors have + different dimensions and/or screens are not virtually aligned. In Qt we want the available + geometry per monitor (QScreen), not desktop (represented by _NET_WORKAREA). WM specification + does not have an atom for this. Thus, QScreen is limted by the lack of support from underlying + system. + + One option could be that Qt does WM's job of calculating this by subtracting geometries of + _NET_WM_STRUT_PARTIAL and windows where _NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_DOCK. + But this won't work on Gnome 3 shell, as it seems that on this desktop environment tool panel + is painted directly on the root window. Maybe there is some Gnome/GTK API that could be used + to get height of the panel, but I did not find one. Maybe other WMs have their own tricks, so + the reliability of this approach is questionable. + */ QRect QXcbVirtualDesktop::getWorkArea() const { QRect r; @@ -395,6 +414,20 @@ quint8 QXcbScreen::depthOfVisual(xcb_visualid_t visualid) const return *it; } +QRect QXcbScreen::availableGeometry() const +{ + static bool isGnome = [this]() { + QXcbIntegration *integration = QXcbIntegration::instance(); + QByteArray desktopEnvironment = integration->services()->desktopEnvironment(); + return desktopEnvironment.toUpper().contains("GNOME"); + }(); + static bool enforceNetWorkarea = !qEnvironmentVariableIsEmpty("QT_RELY_ON_NET_WORKAREA_ATOM"); + bool isMultiHeadSystem = virtualSiblings().length() > 1; + // FIXME: workaround for QTBUG-60513. + bool useScreenGeometry = isGnome && isMultiHeadSystem && !enforceNetWorkarea; + return useScreenGeometry ? m_geometry : m_availableGeometry; +} + QImage::Format QXcbScreen::format() const { return QImage::Format_RGB32; diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 7376ddf35c3..cde6974a5b9 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -114,7 +114,7 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE { return m_geometry; } - QRect availableGeometry() const Q_DECL_OVERRIDE {return m_availableGeometry;} + QRect availableGeometry() const Q_DECL_OVERRIDE; int depth() const Q_DECL_OVERRIDE { return screen()->root_depth; } QImage::Format format() const Q_DECL_OVERRIDE; QSizeF physicalSize() const Q_DECL_OVERRIDE { return m_sizeMillimeters; } diff --git a/src/widgets/kernel/qdesktopwidget.qdoc b/src/widgets/kernel/qdesktopwidget.qdoc index abdbd35f5bb..6c2841db9e0 100644 --- a/src/widgets/kernel/qdesktopwidget.qdoc +++ b/src/widgets/kernel/qdesktopwidget.qdoc @@ -152,7 +152,7 @@ on OS X, or the task bar on Windows). The default screen is used if \a screen is -1. - \sa screenNumber(), screenGeometry() + \sa screenNumber(), screenGeometry(), QScreen::availableGeometry() */ /*! From 6055299378e146ebc245cdd10a1e403870b3ddf5 Mon Sep 17 00:00:00 2001 From: Wayne Arnold Date: Tue, 22 Aug 2017 14:04:07 -0400 Subject: [PATCH 5/6] Partial fix for MAYA-84556 Torn-off menu on small screen can't be moved around Full fix here https://codereview.qt-project.org/#/c/202524/ --- src/gui/kernel/qplatformtheme.cpp | 35 +++ src/gui/kernel/qplatformtheme.h | 1 + .../cocoa/qcocoacolordialoghelper.mm | 6 +- .../platforms/cocoa/qcocoafiledialoghelper.mm | 4 +- .../platforms/cocoa/qcocoafontdialoghelper.mm | 6 +- src/plugins/platforms/cocoa/qcocoahelpers.h | 1 - src/plugins/platforms/cocoa/qcocoahelpers.mm | 40 +-- src/plugins/platforms/cocoa/qcocoamenuitem.mm | 4 +- src/plugins/platforms/cocoa/qt_mac_p.h | 2 - src/plugins/platforms/ios/qiosmenu.h | 3 - src/plugins/platforms/ios/qiosmenu.mm | 38 +-- src/widgets/widgets/qmenu.cpp | 276 ++++++++++++------ src/widgets/widgets/qmenu_p.h | 59 +--- 13 files changed, 249 insertions(+), 226 deletions(-) diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index ce8548f628b..61dacfa076c 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -682,6 +682,41 @@ QString QPlatformTheme::defaultStandardButtonText(int button) return QString(); } +QString QPlatformTheme::removeMnemonics(const QString &original) +{ + QString returnText(original.size(), 0); + int finalDest = 0; + int currPos = 0; + int l = original.length(); + while (l) { + if (original.at(currPos) == QLatin1Char('&') + && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { + ++currPos; + --l; + if (l == 0) + break; + } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && + original.at(currPos + 1) == QLatin1Char('&') && + original.at(currPos + 2) != QLatin1Char('&') && + original.at(currPos + 3) == QLatin1Char(')')) { + /* remove mnemonics its format is "\s*(&X)" */ + int n = 0; + while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) + ++n; + finalDest -= n; + currPos += 4; + l -= 4; + continue; + } + returnText[finalDest] = original.at(currPos); + ++currPos; + ++finalDest; + --l; + } + returnText.truncate(finalDest); + return returnText; +} + unsigned QPlatformThemePrivate::currentKeyPlatforms() { const uint keyboardScheme = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::KeyboardScheme).toInt(); diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h index 36fa7a65aa1..2bd35491fbc 100644 --- a/src/gui/kernel/qplatformtheme.h +++ b/src/gui/kernel/qplatformtheme.h @@ -303,6 +303,7 @@ class Q_GUI_EXPORT QPlatformTheme static QVariant defaultThemeHint(ThemeHint hint); static QString defaultStandardButtonText(int button); + static QString removeMnemonics(const QString &original); protected: explicit QPlatformTheme(QPlatformThemePrivate *priv); diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm index 8843e009a22..ddc29388553 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -31,13 +31,13 @@ ** ****************************************************************************/ -#include "qcocoacolordialoghelper.h" - #ifndef QT_NO_COLORDIALOG #include #include +#include +#include "qcocoacolordialoghelper.h" #include "qcocoahelpers.h" #import @@ -52,7 +52,7 @@ [button setButtonType:NSMomentaryLightButton]; [button setBezelStyle:NSRoundedBezelStyle]; [button setTitle:(NSString*)(CFStringRef)QCFString( - qt_mac_removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; + QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; [[button cell] setFont:[NSFont systemFontOfSize: [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; [superview addSubview:button]; diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 4c1b190b9c9..10eadd01525 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -31,6 +31,8 @@ ** ****************************************************************************/ +#include + #include "qcocoafiledialoghelper.h" #ifndef QT_NO_FILEDIALOG @@ -183,7 +185,7 @@ - (void)dealloc static QString strippedText(QString s) { s.remove( QString::fromLatin1("...") ); - return qt_mac_removeMnemonics(s).trimmed(); + return QPlatformTheme::removeMnemonics(s).trimmed(); } - (NSString *)strip:(const QString &)label diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm index d1802fe4f99..c1c871f02b2 100644 --- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm @@ -31,17 +31,17 @@ ** ****************************************************************************/ -#include "qcocoafontdialoghelper.h" - #ifndef QT_NO_FONTDIALOG #include #include +#include #include #include #include +#include "qcocoafontdialoghelper.h" #include "qcocoahelpers.h" #import @@ -72,7 +72,7 @@ [button setButtonType:NSMomentaryLightButton]; [button setBezelStyle:NSRoundedBezelStyle]; [button setTitle:(NSString*)(CFStringRef)QCFString( - qt_mac_removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; + QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; [[button cell] setFont:[NSFont systemFontOfSize: [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; [superview addSubview:button]; diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index c2abe4e2457..8ffc9879555 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -89,7 +89,6 @@ Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); // Misc void qt_mac_transformProccessToForegroundApplication(); -QString qt_mac_removeMnemonics(const QString &original); CGColorSpaceRef qt_mac_genericColorSpace(); CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget); CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 58d29611117..573140dec62 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -31,6 +31,8 @@ ** ****************************************************************************/ +#include + #include "qcocoahelpers.h" @@ -350,42 +352,6 @@ void qt_mac_transformProccessToForegroundApplication() } } } - -QString qt_mac_removeMnemonics(const QString &original) -{ - QString returnText(original.size(), 0); - int finalDest = 0; - int currPos = 0; - int l = original.length(); - while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { - ++currPos; - --l; - if (l == 0) - break; - } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && - original.at(currPos + 1) == QLatin1Char('&') && - original.at(currPos + 2) != QLatin1Char('&') && - original.at(currPos + 3) == QLatin1Char(')')) { - /* remove mnemonics its format is "\s*(&X)" */ - int n = 0; - while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) - ++n; - finalDest -= n; - currPos += 4; - l -= 4; - continue; - } - returnText[finalDest] = original.at(currPos); - ++currPos; - ++finalDest; - --l; - } - returnText.truncate(finalDest); - return returnText; -} - static CGColorSpaceRef m_genericColorSpace = 0; static QHash m_displayColorSpaceHash; static bool m_postRoutineRegistered = false; @@ -640,7 +606,7 @@ bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret) QString qt_mac_removeAmpersandEscapes(QString s) { - return qt_mac_removeMnemonics(s).trimmed(); + return QPlatformTheme::removeMnemonics(s).trimmed(); } /*! \internal diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 49f3da48c21..8a80d17ea18 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -31,6 +31,8 @@ ** ****************************************************************************/ +#include + #include "qcocoamenuitem.h" #include "qcocoamenu.h" @@ -305,7 +307,7 @@ NSUInteger keySequenceModifierMask(const QKeySequence &accel) if (accel.count() > 1) text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")"); - QString finalString = qt_mac_removeMnemonics(text); + QString finalString = QPlatformTheme::removeMnemonics(text); bool useAttributedTitle = false; // Cocoa Font and title if (m_font.resolve()) { diff --git a/src/plugins/platforms/cocoa/qt_mac_p.h b/src/plugins/platforms/cocoa/qt_mac_p.h index 299678eee86..91db44e100a 100644 --- a/src/plugins/platforms/cocoa/qt_mac_p.h +++ b/src/plugins/platforms/cocoa/qt_mac_p.h @@ -104,8 +104,6 @@ class QMacSmartQuickDrawRegion } }; -QString qt_mac_removeMnemonics(const QString &original); //implemented in qmacstyle_mac.cpp - class Q_WIDGETS_EXPORT QMacWindowChangeEvent { private: diff --git a/src/plugins/platforms/ios/qiosmenu.h b/src/plugins/platforms/ios/qiosmenu.h index ec23b555078..3beb58b5031 100644 --- a/src/plugins/platforms/ios/qiosmenu.h +++ b/src/plugins/platforms/ios/qiosmenu.h @@ -74,9 +74,6 @@ class QIOSMenuItem : public QPlatformMenuItem bool m_separator; QIOSMenu *m_menu; QKeySequence m_shortcut; - -private: - QString removeMnemonics(const QString &original); }; typedef QList QIOSMenuItemList; diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index 612f8c43a27..5f9346816a1 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -33,6 +33,7 @@ #include #include +#include #include "qiosglobal.h" #include "qiosmenu.h" @@ -271,7 +272,7 @@ - (void)cancelMenu void QIOSMenuItem::setText(const QString &text) { - m_text = removeMnemonics(text); + m_text = QPlatformTheme::removeMnemonics(text); } void QIOSMenuItem::setMenu(QPlatformMenu *menu) @@ -304,41 +305,6 @@ - (void)cancelMenu m_enabled = enabled; } -QString QIOSMenuItem::removeMnemonics(const QString &original) -{ - // Copied from qcocoahelpers - QString returnText(original.size(), 0); - int finalDest = 0; - int currPos = 0; - int l = original.length(); - while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { - ++currPos; - --l; - if (l == 0) - break; - } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && - original.at(currPos + 1) == QLatin1Char('&') && - original.at(currPos + 2) != QLatin1Char('&') && - original.at(currPos + 3) == QLatin1Char(')')) { - /* remove mnemonics its format is "\s*(&X)" */ - int n = 0; - while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) - ++n; - finalDest -= n; - currPos += 4; - l -= 4; - continue; - } - returnText[finalDest] = original.at(currPos); - ++currPos; - ++finalDest; - --l; - } - returnText.truncate(finalDest); - return returnText; -} QIOSMenu::QIOSMenu() : QPlatformMenu() diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 2bb8d786274..b79341b8e43 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -66,6 +66,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -87,14 +88,11 @@ class QTornOffMenu : public QMenu causedStack = p->d_func()->calcCausedStack(); } - void setMenuSize(const QSize &menuSize) { + void setMenuSize(const QRect &screen) { Q_Q(QTornOffMenu); - QSize size = menuSize; - const QPoint p = (!initialized) ? causedMenu->pos() : q->pos(); - QRect screen = popupGeometry(QApplication::desktop()->screenNumber(p)); - const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q); - const int titleBarHeight = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, 0, q); + QSize size = q->sizeHint(); if (scroll && (size.height() > screen.height() - titleBarHeight || size.width() > screen.width())) { + const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q); const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuHMargin, 0, q); scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown); @@ -108,23 +106,30 @@ class QTornOffMenu : public QMenu QPointer causedMenu; QVector > causedStack; bool initialized; + int titleBarHeight; }; + public: QTornOffMenu(QMenu *p) : QMenu(*(new QTornOffMenuPrivate(p))) { Q_D(QTornOffMenu); // make the torn-off menu a sibling of p (instead of a child) - QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.last(); + QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.constLast(); if (parentWidget->parentWidget()) parentWidget = parentWidget->parentWidget(); - setParent(parentWidget, Qt::Window | Qt::Tool); + setParent(parentWidget, Qt::Tool); setAttribute(Qt::WA_DeleteOnClose, true); setAttribute(Qt::WA_X11NetWmWindowTypeMenu, true); - setWindowTitle(p->windowTitle()); + updateWindowTitle(); setEnabled(p->isEnabled()); setStyleSheet(p->styleSheet()); if (style() != p->style()) - setStyle(p->style()); + setStyle(p->style()); + + QStyleOption opt; + opt.init(this); + d->titleBarHeight = style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, this); + int leftMargin, topMargin, rightMargin, bottomMargin; p->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); setContentsMargins(leftMargin, topMargin, rightMargin, bottomMargin); @@ -134,9 +139,40 @@ class QTornOffMenu : public QMenu QList items = p->actions(); for(int i = 0; i < items.count(); i++) addAction(items.at(i)); - d->setMenuSize(sizeHint()); d->initialized = true; + + // Force recomputing action rects so that the tool window frame is taken into account. + d->itemsDirty = true; + // For this, we need early creation so that the frame geometry is correct. + createWinId(); + // Finally, piggyback on actionEvent() to set the popup size. + actionEvent(nullptr); } + + bool event(QEvent *e) override + { + if (e->type() == QEvent::Resize) { + // No need to set the mask since it's a tool window. Also, we just + // updated the action rects. So we can skip the logic in QMenu::event(). + + // FIXME: We call updateActionRects() more times than it seems necessary, + // and sometimes we even end up using the wrong screen geometry. This is + // the fastest workaround until we can sit and clean this logic. + return QWidget::event(e); + } + + if (e->type() == QEvent::ScreenChangeInternal) { + Q_D(QTornOffMenu); + const int screenNumber = QGuiApplication::screens().indexOf(windowHandle()->screen()); + const auto screenRect = d->popupGeometry(screenNumber); + d->itemsDirty = true; // Force recomputing action rects in the new screen + d->updateActionRects(screenRect); + d->setMenuSize(screenRect); + } + + return QMenu::event(e); + } + void syncWithMenu(QMenu *menu, QActionEvent *act) { Q_D(QTornOffMenu); @@ -147,17 +183,31 @@ class QTornOffMenu : public QMenu } else if (act->type() == QEvent::ActionRemoved) removeAction(act->action()); } + void actionEvent(QActionEvent *e) Q_DECL_OVERRIDE { Q_D(QTornOffMenu); - QMenu::actionEvent(e); + if (e) + QMenu::actionEvent(e); if (d->initialized) { - d->setMenuSize(sizeHint()); + const auto screenRect = d->popupGeometry(QApplication::desktop()->screenNumber(pos())); + d->setMenuSize(screenRect); } } + + void updateWindowTitle() + { + Q_D(QTornOffMenu); + if (!d->causedMenu) + return; + const QString &cleanTitle = QPlatformTheme::removeMnemonics(d->causedMenu->title()).trimmed(); + setWindowTitle(cleanTitle); + } + public slots: void onTrigger(QAction *action) { d_func()->activateAction(action, QAction::Trigger, false); } void onHovered(QAction *action) { d_func()->activateAction(action, QAction::Hover, false); } + private: Q_DECLARE_PRIVATE(QTornOffMenu) friend class QMenuPrivate; @@ -172,18 +222,29 @@ void QMenuPrivate::init() q->setAttribute(Qt::WA_X11NetWmWindowTypePopupMenu); defaultMenuAction = menuAction = new QAction(q); menuAction->d_func()->menu = q; + QObject::connect(menuAction, &QAction::changed, [=] { + if (!tornPopup.isNull()) + tornPopup->updateWindowTitle(); + }); q->setMouseTracking(q->style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, q)); if (q->style()->styleHint(QStyle::SH_Menu_Scrollable, 0, q)) { scroll = new QMenuPrivate::QMenuScroller; scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone; } - setPlatformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu()); sloppyState.initialize(q); delayState.initialize(q); mousePopupDelay = q->style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, q); } +QPlatformMenu *QMenuPrivate::createPlatformMenu() +{ + Q_Q(QMenu); + if (platformMenu.isNull()) + q->setPlatformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu()); + return platformMenu.data(); +} + void QMenuPrivate::setPlatformMenu(QPlatformMenu *menu) { Q_Q(QMenu); @@ -295,10 +356,17 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q); const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0; const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + tearoffHeight; + + // Torn off menu windows should be adjusted by their frame height, otherwise some + // window managers may behave unexpectedly if the window is taller than the screen. + int frameHeight = 0; + if (!tearoff && (q->windowFlags() & Qt::WindowType_Mask) == Qt::Tool) + frameHeight = q->geometry().top() - q->frameGeometry().top(); + + const int column_max_y = screen.height() - 2 * deskFw - (vmargin + bottommargin + fw + frameHeight); int max_column_width = 0; - int dh = screen.height(); int y = base_y; - + //for compatibility now - will have to refactor this away tabWidth = 0; maxIconWidth = 0; @@ -374,8 +442,7 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const if (!sz.isEmpty()) { max_column_width = qMax(max_column_width, sz.width()); //wrapping - if (!scroll && - y + sz.height() + vmargin + bottommargin + fw > dh - (deskFw * 2)) { + if (!scroll && y + sz.height() > column_max_y) { ncols++; y = base_y; } else { @@ -401,8 +468,7 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const QRect &rect = actionRects[i]; if (rect.isNull()) continue; - if (!scroll && - y + rect.height() + vmargin + bottommargin + fw > dh - deskFw * 2) { + if (!scroll && y + rect.height() > column_max_y) { x += max_column_width + hmargin; y = base_y; } @@ -420,18 +486,6 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const itemsDirty = 0; } -QSize QMenuPrivate::adjustMenuSizeForScreen(const QRect &screen) -{ - Q_Q(QMenu); - QSize ret = screen.size(); - itemsDirty = true; - updateActionRects(screen); - const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); - const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuHMargin, 0, q); - ret.setWidth(qMin(actionRects.at(getLastVisibleAction()).right() + fw + hmargin + rightmargin + 1, screen.width())); - return ret; -} - int QMenuPrivate::getLastVisibleAction() const { //let's try to get the last visible action @@ -663,6 +717,7 @@ void QMenuSloppyState::reset() m_enabled = false; m_first_mouse = true; m_init_guard = false; + m_use_reset_action = true; m_uni_dir_discarded_count = 0; m_time.stop(); m_reset_action = Q_NULLPTR; @@ -715,6 +770,7 @@ void QMenuSloppyState::setSubMenuPopup(const QRect &actionRect, QAction *resetAc { m_enabled = true; m_init_guard = true; + m_use_reset_action = true; m_time.stop(); m_action_rect = actionRect; m_sub_menu = subMenu; @@ -775,10 +831,12 @@ void QMenuSloppyState::timeout() if (m_sub_menu) menu_priv->hideMenu(m_sub_menu); - if (reallyHasMouse) - menu_priv->setCurrentAction(m_reset_action,0); - else + if (reallyHasMouse) { + if (m_use_reset_action) + menu_priv->setCurrentAction(m_reset_action, 0); + } else { menu_priv->setCurrentAction(Q_NULLPTR, 0); + } } //return the top causedPopup.widget that is not a QMenu @@ -896,7 +954,7 @@ QRect QMenuPrivate::rect() const const int vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q); const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q); return (q->rect().adjusted(hmargin + fw + leftmargin, vmargin + fw + topmargin, - -(hmargin + fw + rightmargin), -(vmargin + fw + bottommargin))); + -(hmargin + fw + rightmargin), -(vmargin + fw + bottommargin))); } QMenuPrivate::ScrollerTearOffItem::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::Type type, QMenuPrivate *mPrivate, QWidget *parent, Qt::WindowFlags f) @@ -936,6 +994,7 @@ void QMenuPrivate::ScrollerTearOffItem::updateScrollerRects(const QRect &rect) } } + /*! Returns the action associated with this menu. */ @@ -1032,7 +1091,7 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc } if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) { - newOffset = q->height() - (saccum - newOffset) - fw * 2 - vmargin - topmargin - bottommargin; //last item at bottom + newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin - topmargin - bottommargin; //last item at bottom if (tearoff) newOffset -= q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q); } @@ -1197,7 +1256,7 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) if (scroll && !activeMenu) { //let the scroller "steal" the event bool isScroll = false; if (pos.x() >= 0 && pos.x() < q->width()) { - for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) { + for (int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) { if (scroll->scrollFlags & dir) { if (dir == QMenuScroller::ScrollUp) isScroll = (pos.y() <= scrollerHeight() + fw + vmargin + topmargin); @@ -1219,8 +1278,8 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) } if (tearoff) { //let the tear off thingie "steal" the event.. - QRect tearRect(leftmargin + hmargin + fw, topmargin + vmargin + fw, q->width() - fw * 2 - hmargin * 2 - leftmargin - rightmargin, - q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q)); + QRect tearRect(leftmargin + hmargin + fw, topmargin + vmargin + fw, q->width() - fw * 2 - hmargin * 2 -leftmargin - rightmargin, + q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q)); if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) tearRect.translate(0, scrollerHeight()); q->update(tearRect); @@ -1411,14 +1470,17 @@ void QMenuPrivate::_q_platformMenuAboutToShow() Q_Q(QMenu); #ifdef Q_OS_OSX - if (platformMenu) - Q_FOREACH (QAction *action, q->actions()) + if (platformMenu) { + const auto actions = q->actions(); + for (QAction *action : actions) { if (QWidget *widget = widgetItems.value(action)) if (widget->parent() == q) { QPlatformMenuItem *menuItem = platformMenu->menuItemForTag(reinterpret_cast(action)); moveWidgetToPlatformItem(widget, menuItem); platformMenu->syncMenuItem(menuItem); } + } + } #endif emit q->aboutToShow(); @@ -1547,7 +1609,7 @@ void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) When inserting action items you usually specify a receiver and a slot. The receiver will be notifed whenever the item is \l{QAction::triggered()}{triggered()}. In addition, QMenu provides - two signals, activated() and highlighted(), which signal the + two signals, triggered() and hovered(), which signal the QAction that was triggered from the menu. You clear a menu with clear() and remove individual action items @@ -2254,6 +2316,7 @@ QSize QMenu::sizeHint() const if (rect.right() >= s.width()) s.setWidth(rect.x() + rect.width()); } + // Note that the action rects calculated above already include // the top and left margins, so we only need to add margins for // the bottom and right. @@ -2304,15 +2367,14 @@ void QMenu::popup(const QPoint &p, QAction *atAction) ensurePolished(); // Get the right font emit aboutToShow(); const bool actionListChanged = d->itemsDirty; - d->updateActionRects(); - QPoint pos; - QPushButton *causedButton = qobject_cast(d->causedPopup.widget); - if (actionListChanged && causedButton) - pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition(); - else - pos = p; - QSize size = sizeHint(); + // Give the poor guy a hand so that we can finally use the + // right screen as a basis for computing the action rects. + if (!internalWinId()) { + createWinId(); + d->itemsDirty = true; + } + QRect screen; #ifndef QT_NO_GRAPHICSVIEW bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this); @@ -2321,17 +2383,26 @@ void QMenu::popup(const QPoint &p, QAction *atAction) else #endif screen = d->popupGeometry(QApplication::desktop()->screenNumber(p)); + d->updateActionRects(screen); + + QPoint pos; + QPushButton *causedButton = qobject_cast(d->causedPopup.widget); + if (actionListChanged && causedButton) + pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition(); + else + pos = p; + + const QSize menuSizeHint(sizeHint()); + QSize size = menuSizeHint; const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this); bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen); // if the screens have very different geometries and the menu is too big, we have to recalculate - if (size.height() > screen.height() || size.width() > screen.width()) { - size = d->adjustMenuSizeForScreen(screen); - adjustToDesktop = true; - } - // Layout is not right, we might be able to save horizontal space - if (d->ncols >1 && size.height() < screen.height()) { - size = d->adjustMenuSizeForScreen(screen); + if ((size.height() > screen.height() || size.width() > screen.width()) || + // Layout is not right, we might be able to save horizontal space + (d->ncols >1 && size.height() < screen.height())) { + size.setWidth(qMin(menuSizeHint.width(), screen.width() - desktopFrame * 2)); + size.setHeight(qMin(menuSizeHint.height(), screen.height() - desktopFrame * 2)); adjustToDesktop = true; } @@ -2384,7 +2455,6 @@ void QMenu::popup(const QPoint &p, QAction *atAction) d->mousePopupPos = mouse; const bool snapToMouse = !d->causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse)); - const QSize menuSize(sizeHint()); if (adjustToDesktop) { // handle popup falling "off screen" if (isRightToLeft()) { @@ -2418,7 +2488,7 @@ void QMenu::popup(const QPoint &p, QAction *atAction) if (pos.y() < screen.top() + desktopFrame) pos.setY(screen.top() + desktopFrame); - if (pos.y() + menuSize.height() - 1 > screen.bottom() - desktopFrame) { + if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) { if (d->scroll) { d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown); int y = qMax(screen.y(),pos.y()); @@ -2431,29 +2501,29 @@ void QMenu::popup(const QPoint &p, QAction *atAction) } const int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this); QMenu *caused = qobject_cast(d_func()->causedPopup.widget); - if (caused && caused->geometry().width() + menuSize.width() + subMenuOffset < screen.width()) { + if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) { QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction)); const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft()); parentActionRect.moveTopLeft(actionTopLeft); if (isRightToLeft()) { - if ((pos.x() + menuSize.width() > parentActionRect.left() - subMenuOffset) + if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset) && (pos.x() < parentActionRect.right())) { - pos.rx() = parentActionRect.left() - menuSize.width(); + pos.rx() = parentActionRect.left() - menuSizeHint.width(); if (pos.x() < screen.x()) pos.rx() = parentActionRect.right(); - if (pos.x() + menuSize.width() > screen.x() + screen.width()) + if (pos.x() + menuSizeHint.width() > screen.x() + screen.width()) pos.rx() = screen.x(); } } else { if ((pos.x() < parentActionRect.right() + subMenuOffset) - && (pos.x() + menuSize.width() > parentActionRect.left())) + && (pos.x() + menuSizeHint.width() > parentActionRect.left())) { pos.rx() = parentActionRect.right(); - if (pos.x() + menuSize.width() > screen.x() + screen.width()) - pos.rx() = parentActionRect.left() - menuSize.width(); + if (pos.x() + menuSizeHint.width() > screen.x() + screen.width()) + pos.rx() = parentActionRect.left() - menuSizeHint.width(); if (pos.x() < screen.x()) - pos.rx() = screen.x() + screen.width() - menuSize.width(); + pos.rx() = screen.x() + screen.width() - menuSizeHint.width(); } } } @@ -2675,7 +2745,7 @@ void QMenu::paintEvent(QPaintEvent *e) //calculate the scroll up / down rect const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this); - const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin, 0, this); + const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,0, this); const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin, 0, this); QRect scrollUpRect, scrollDownRect; @@ -2689,7 +2759,7 @@ void QMenu::paintEvent(QPaintEvent *e) if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin, - contentWidth, d->scrollerHeight()); + contentWidth, d->scrollerHeight()); } //calculate the tear off rect @@ -2714,10 +2784,10 @@ void QMenu::paintEvent(QPaintEvent *e) QRect adjustedActionRect = actionRect; if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top()) - continue; + continue; - if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom()) - continue; + if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom()) + continue; if (adjustedActionRect.intersects(scrollUpTearOffRect)) { if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom()) @@ -2903,7 +2973,10 @@ QMenu::event(QEvent *e) if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return - || kev->matches(QKeySequence::Cancel)) { +#ifndef QT_NO_SHORTCUT + || kev->matches(QKeySequence::Cancel) +#endif + ) { e->accept(); return true; } @@ -2916,10 +2989,15 @@ QMenu::event(QEvent *e) return true; } } break; - case QEvent::ContextMenu: - if (d->delayState.timer.isActive()) { - d->delayState.stop(); - internalDelayedPopup(); + case QEvent::MouseButtonPress: + case QEvent::ContextMenu: { + bool canPopup = true; + if (e->type() == QEvent::MouseButtonPress) + canPopup = (static_cast(e)->button() == Qt::LeftButton); + if (canPopup && d->delayState.timer.isActive()) { + d->delayState.stop(); + internalDelayedPopup(); + } } break; case QEvent::Resize: { @@ -3146,7 +3224,7 @@ void QMenu::keyPressEvent(QKeyEvent *e) key_consumed = true; break; } - //FALL THROUGH + // FALLTHROUGH case Qt::Key_Left: { if (d->currentAction && !d->scroll) { QAction *nextAction = 0; @@ -3225,7 +3303,11 @@ void QMenu::keyPressEvent(QKeyEvent *e) key_consumed = false; } - if (!key_consumed && (e->matches(QKeySequence::Cancel) + if (!key_consumed && ( + false +#ifndef QT_NO_SHORTCUT + || e->matches(QKeySequence::Cancel) +#endif #ifdef QT_KEYPAD_NAVIGATION || e->key() == Qt::Key_Back #endif @@ -3439,7 +3521,9 @@ static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem *i item->setIcon(QIcon()); } item->setVisible(action->isVisible()); +#ifndef QT_NO_SHORTCUT item->setShortcut(action->shortcut()); +#endif item->setCheckable(action->isCheckable()); item->setChecked(action->isChecked()); item->setFont(action->font()); @@ -3473,7 +3557,7 @@ void QMenu::actionEvent(QActionEvent *e) if (QWidgetAction *wa = qobject_cast(e->action())) { QWidget *widget = wa->requestWidget(this); if (widget) { - d->widgetItems.insert(wa, widget); + d->widgetItems.insert(wa, widget); if (d->scroll) { if (!d->scrollUpTearOffItem) d->scrollUpTearOffItem = @@ -3513,7 +3597,9 @@ void QMenu::actionEvent(QActionEvent *e) QObject::connect(menuItem, SIGNAL(activated()), e->action(), SLOT(trigger())); QObject::connect(menuItem, SIGNAL(hovered()), e->action(), SIGNAL(hovered())); copyActionToPlatformItem(e->action(), menuItem, d->platformMenu); - QPlatformMenuItem* beforeItem = d->platformMenu->menuItemForTag(reinterpret_cast(e->before())); + QPlatformMenuItem *beforeItem = e->before() + ? d->platformMenu->menuItemForTag(reinterpret_cast(e->before())) + : nullptr; d->platformMenu->insertMenuItem(menuItem, beforeItem); } else if (e->type() == QEvent::ActionRemoved) { QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(reinterpret_cast(e->action())); @@ -3530,17 +3616,6 @@ void QMenu::actionEvent(QActionEvent *e) d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators); } -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) - if (!d->wce_menu) - d->wce_menu = new QMenuPrivate::QWceMenuPrivate; - if (e->type() == QEvent::ActionAdded) - d->wce_menu->addAction(e->action(), d->wce_menu->findAction(e->before())); - else if (e->type() == QEvent::ActionRemoved) - d->wce_menu->removeAction(e->action()); - else if (e->type() == QEvent::ActionChanged) - d->wce_menu->syncAction(e->action()); -#endif - if (isVisible()) { resize(sizeHint()); update(); @@ -3568,11 +3643,22 @@ void QMenu::internalDelayedPopup() d->activeMenu->d_func()->causedPopup.widget = this; d->activeMenu->d_func()->causedPopup.action = d->currentAction; + QRect screen; +#ifndef QT_NO_GRAPHICSVIEW + bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this); + if (isEmbedded) + screen = d->popupGeometry(this); + else +#endif + screen = d->popupGeometry(QApplication::desktop()->screenNumber(pos())); + int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this); const QRect actionRect(d->actionRect(d->currentAction)); - const QPoint rightPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top()))); + QPoint subMenuPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top()))); + if (subMenuPos.x() > screen.right()) + subMenuPos.setX(QCursor::pos().x()); - d->activeMenu->popup(rightPos); + d->activeMenu->popup(subMenuPos); d->sloppyState.setSubMenuPopup(actionRect, d->currentAction, d->activeMenu); #if !defined(Q_OS_DARWIN) diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index 9323f907d74..6464c6e92be 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -62,15 +62,6 @@ QT_BEGIN_NAMESPACE class QTornOffMenu; class QEventLoop; -#ifdef Q_OS_WINCE -struct QWceMenuAction { - uint command; - QPointer action; - HMENU menuHandle; - QWceMenuAction() : menuHandle(0), command(0) {} -}; -#endif - template class QSetValueOnDestroy { @@ -97,6 +88,7 @@ class QMenuSloppyState , m_select_other_actions(false) , m_first_mouse(true) , m_init_guard(false) + , m_use_reset_action(true) , m_uni_dir_discarded_count(0) , m_uni_dir_fail_at_count(0) , m_timeout(0) @@ -184,9 +176,17 @@ class QMenuSloppyState QSetValueOnDestroy setFirstMouse(m_first_mouse, false); QSetValueOnDestroy setPreviousPoint(m_previous_point, mousePos); - if (resetAction && resetAction->isSeparator()) + if (resetAction && resetAction->isSeparator()) { m_reset_action = Q_NULLPTR; - else { + m_use_reset_action = true; + } else if (m_reset_action != resetAction) { + if (m_use_reset_action && resetAction) { + const QList actions = m_menu->actions(); + const int resetIdx = actions.indexOf(resetAction); + const int originIdx = actions.indexOf(m_origin_action); + if (resetIdx > -1 && originIdx > -1 && qAbs(resetIdx - originIdx) > 1) + m_use_reset_action = false; + } m_reset_action = resetAction; } @@ -251,6 +251,7 @@ class QMenuSloppyState bool m_init_guard; bool m_discard_state_when_entering_parent; bool m_dont_start_time_on_leave; + bool m_use_reset_action; short m_uni_dir_discarded_count; short m_uni_dir_fail_at_count; short m_timeout; @@ -280,21 +281,16 @@ class QMenuPrivate : public QWidgetPrivate scroll(0), eventLoop(0), tearoff(0), tornoff(0), tearoffHighlighted(0), hasCheckableItems(0), doChildEffects(false), platformMenu(0), scrollUpTearOffItem(nullptr), scrollDownItem(nullptr) - -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) - ,wce_menu(0) -#endif { } + ~QMenuPrivate() { delete scroll; if (!platformMenu.isNull() && !platformMenu->parent()) delete platformMenu.data(); -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) - delete wce_menu; -#endif } void init(); + QPlatformMenu *createPlatformMenu(); void setPlatformMenu(QPlatformMenu *menu); void syncPlatformMenu(); #ifdef Q_OS_OSX @@ -318,7 +314,6 @@ class QMenuPrivate : public QWidgetPrivate mutable uint ncols : 4; //4 bits is probably plenty uint collapsibleSeparators : 1; uint toolTipsVisible : 1; - QSize adjustMenuSizeForScreen(const QRect & screen); int getLastVisibleAction() const; bool activationRecursionGuard; @@ -452,32 +447,8 @@ class QMenuPrivate : public QWidgetPrivate QPointer actionAboutToTrigger; -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) - struct QWceMenuPrivate { - QList actionItems; - HMENU menuHandle; - QWceMenuPrivate(); - ~QWceMenuPrivate(); - void addAction(QAction *, QWceMenuAction* =0); - void addAction(QWceMenuAction *, QWceMenuAction* =0); - void syncAction(QWceMenuAction *); - inline void syncAction(QAction *a) { syncAction(findAction(a)); } - void removeAction(QWceMenuAction *); - void rebuild(); - inline void removeAction(QAction *a) { removeAction(findAction(a)); } - inline QWceMenuAction *findAction(QAction *a) { - for(int i = 0; i < actionItems.size(); i++) { - QWceMenuAction *act = actionItems[i]; - if(a == act->action) - return act; - } - return 0; - } - } *wce_menu; - HMENU wceMenu(); - QAction* wceCommands(uint command); -#endif QPointer noReplayFor; + class ScrollerTearOffItem : public QWidget { public: enum Type { ScrollUp, ScrollDown }; From 7ed11459b252f8a1193ffec5b94e28cc80ced56a Mon Sep 17 00:00:00 2001 From: Wayne Arnold Date: Wed, 23 Aug 2017 09:19:55 -0400 Subject: [PATCH 6/6] DLL version bump for Maya 2018 update 1 5.6.1.3 -> 5.6.1.4 --- qmake/generators/win32/winmakefile.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qmake/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp index 448b0abd62b..f10b361b5b8 100644 --- a/qmake/generators/win32/winmakefile.cpp +++ b/qmake/generators/win32/winmakefile.cpp @@ -264,9 +264,10 @@ void Win32MakefileGenerator::processRcFileVar() QStringList vers = project->first("VERSION").toQString().split(".", QString::SkipEmptyParts); //it's "0" here for Maya2017, "1" for Update2, "2" for update3. "3" for update4. - //"5.6.1.2"->"5.6.1.3" + //it's 4 for 2018 update 1 + //"5.6.1.3"->"5.6.1.4" for (int i = vers.size(); i < 4; i++) - vers += "3"; + vers += "4"; QString versionString = vers.join('.'); QStringList rcIcons;