diff options
Diffstat (limited to 'src/widgets')
27 files changed, 657 insertions, 148 deletions
diff --git a/src/widgets/effects/qpixmapfilter.cpp b/src/widgets/effects/qpixmapfilter.cpp index 6f618b8914..e4e409a542 100644 --- a/src/widgets/effects/qpixmapfilter.cpp +++ b/src/widgets/effects/qpixmapfilter.cpp @@ -705,7 +705,8 @@ void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transp Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied || img.format() == QImage::Format_RGB32 - || img.format() == QImage::Format_Indexed8); + || img.format() == QImage::Format_Indexed8 + || img.format() == QImage::Format_Grayscale8); // choose the alpha such that pixels at radius distance from a fully // saturated pixel will have an alpha component of no greater than @@ -780,7 +781,7 @@ Q_WIDGETS_EXPORT QImage qt_halfScaled(const QImage &source) QImage srcImage = source; - if (source.format() == QImage::Format_Indexed8) { + if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) { // assumes grayscale QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); @@ -890,7 +891,7 @@ Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, Q_WIDGETS_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0) { - if (blurImage.format() == QImage::Format_Indexed8) + if (blurImage.format() == QImage::Format_Indexed8 || blurImage.format() == QImage::Format_Grayscale8) expblur<12, 10, true>(blurImage, radius, quality, transposed); else expblur<12, 10, false>(blurImage, radius, quality, transposed); diff --git a/src/widgets/graphicsview/qgraphicslayout_p.h b/src/widgets/graphicsview/qgraphicslayout_p.h index a45cb31b28..698118eb75 100644 --- a/src/widgets/graphicsview/qgraphicslayout_p.h +++ b/src/widgets/graphicsview/qgraphicslayout_p.h @@ -65,7 +65,7 @@ inline bool qt_graphicsLayoutDebug() { static int checked_env = -1; if(checked_env == -1) - checked_env = !!qgetenv("QT_GRAPHICSLAYOUT_DEBUG").toInt(); + checked_env = !!qEnvironmentVariableIntValue("QT_GRAPHICSLAYOUT_DEBUG"); return checked_env; } #endif diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index 988152da9d..8765b8114d 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -4910,7 +4910,7 @@ void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const Q if (painterStateProtection || restorePainterClip) painter->restore(); - static int drawRect = qgetenv("QT_DRAW_SCENE_ITEM_RECTS").toInt(); + static int drawRect = qEnvironmentVariableIntValue("QT_DRAW_SCENE_ITEM_RECTS"); if (drawRect) { QPen oldPen = painter->pen(); QBrush oldBrush = painter->brush(); diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 1d8be6398c..a6361bd750 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -1531,6 +1531,7 @@ void QAbstractItemView::setIconSize(const QSize &size) return; d->iconSize = size; d->doDelayedItemsLayout(); + emit iconSizeChanged(size); } QSize QAbstractItemView::iconSize() const diff --git a/src/widgets/itemviews/qabstractitemview.h b/src/widgets/itemviews/qabstractitemview.h index f7cdcc8451..4162479194 100644 --- a/src/widgets/itemviews/qabstractitemview.h +++ b/src/widgets/itemviews/qabstractitemview.h @@ -68,7 +68,7 @@ class Q_WIDGETS_EXPORT QAbstractItemView : public QAbstractScrollArea Q_PROPERTY(bool alternatingRowColors READ alternatingRowColors WRITE setAlternatingRowColors) Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) Q_PROPERTY(SelectionBehavior selectionBehavior READ selectionBehavior WRITE setSelectionBehavior) - Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged) Q_PROPERTY(Qt::TextElideMode textElideMode READ textElideMode WRITE setTextElideMode) Q_PROPERTY(ScrollMode verticalScrollMode READ verticalScrollMode WRITE setVerticalScrollMode) Q_PROPERTY(ScrollMode horizontalScrollMode READ horizontalScrollMode WRITE setHorizontalScrollMode) @@ -255,6 +255,8 @@ Q_SIGNALS: void entered(const QModelIndex &index); void viewportEntered(); + void iconSizeChanged(const QSize &size); + protected: QAbstractItemView(QAbstractItemViewPrivate &, QWidget *parent = 0); diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 2c5f4b7c72..71bf92f321 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -3094,8 +3094,6 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem &viewItem) { - Q_Q(QTreeView); - Q_UNUSED(q) viewItems.insert(pos, count, viewItem); QTreeViewItem *items = viewItems.data(); for (int i = pos + count; i < viewItems.count(); i++) @@ -3105,8 +3103,6 @@ void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem & void QTreeViewPrivate::removeViewItems(int pos, int count) { - Q_Q(QTreeView); - Q_UNUSED(q) viewItems.remove(pos, count); QTreeViewItem *items = viewItems.data(); for (int i = pos; i < viewItems.count(); i++) diff --git a/src/widgets/itemviews/qtreeview_p.h b/src/widgets/itemviews/qtreeview_p.h index bc444eb37f..72cb897353 100644 --- a/src/widgets/itemviews/qtreeview_p.h +++ b/src/widgets/itemviews/qtreeview_p.h @@ -210,6 +210,8 @@ public: QSet<QPersistentModelIndex> hiddenIndexes; inline bool isRowHidden(const QModelIndex &idx) const { + if (hiddenIndexes.isEmpty()) + return false; //We first check if the idx is a QPersistentModelIndex, because creating QPersistentModelIndex is slow return isPersistent(idx) && hiddenIndexes.contains(idx); } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 8f6c5d748c..c913d83213 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -636,7 +636,7 @@ void QApplicationPrivate::initialize() qRegisterGuiStateMachine(); #endif - if (qgetenv("QT_USE_NATIVE_WINDOWS").toInt() > 0) + if (qEnvironmentVariableIntValue("QT_USE_NATIVE_WINDOWS") > 0) QCoreApplication::setAttribute(Qt::AA_NativeWindows); #ifdef Q_OS_WINCE @@ -658,8 +658,12 @@ void QApplicationPrivate::initialize() initializeMultitouch(); if (QApplication::desktopSettingsAware()) - if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { QApplicationPrivate::enabledAnimations = theme->themeHint(QPlatformTheme::UiEffects).toInt(); +#ifndef QT_NO_WHEELEVENT + QApplicationPrivate::wheel_scroll_lines = theme->themeHint(QPlatformTheme::WheelScrollLines).toInt(); +#endif + } is_app_running = true; // no longer starting up } diff --git a/src/widgets/kernel/qdesktopwidget_p.h b/src/widgets/kernel/qdesktopwidget_p.h index 62f4d8ed6f..0f3083cbb5 100644 --- a/src/widgets/kernel/qdesktopwidget_p.h +++ b/src/widgets/kernel/qdesktopwidget_p.h @@ -48,6 +48,8 @@ #include "QDesktopWidget" #include "private/qwidget_p.h" +#include <QtCore/qalgorithms.h> + QT_BEGIN_NAMESPACE class QDesktopScreenWidget : public QWidget { @@ -65,7 +67,7 @@ class QDesktopWidgetPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QDesktopWidget) public: - ~QDesktopWidgetPrivate() {foreach(QDesktopScreenWidget *s, screens) delete s; } + ~QDesktopWidgetPrivate() { qDeleteAll(screens); } void _q_updateScreens(); void _q_availableGeometryChanged(); diff --git a/src/widgets/kernel/qgesture.cpp b/src/widgets/kernel/qgesture.cpp index 2b8f04ccdf..72faedf24e 100644 --- a/src/widgets/kernel/qgesture.cpp +++ b/src/widgets/kernel/qgesture.cpp @@ -36,6 +36,7 @@ #include "private/qstandardgestures_p.h" #include "qgraphicsview.h" +#include <QtCore/QDebug> #ifndef QT_NO_GESTURES QT_BEGIN_NAMESPACE @@ -1124,7 +1125,81 @@ QPointF QGestureEvent::mapToGraphicsScene(const QPointF &gesturePoint) const \sa QEvent::ignore() */ -#endif +#endif // Q_NO_USING_KEYWORD + +#ifndef QT_NO_DEBUG_STREAM + +static void formatGestureHeader(QDebug d, const char *className, const QGesture *gesture) +{ + d << className << "(state=" << gesture->state(); + if (gesture->hasHotSpot()) + d << ",hotSpot=" << gesture->hotSpot(); +} + +Q_WIDGETS_EXPORT QDebug operator<<(QDebug d, const QGesture *gesture) +{ + QDebugStateSaver saver(d); + d.nospace(); + switch (gesture->gestureType()) { + case Qt::TapGesture: + formatGestureHeader(d, "QTapGesture", gesture); + d << ",position=" << static_cast<const QTapGesture*>(gesture)->position() << ')'; + break; + case Qt::TapAndHoldGesture: { + const QTapAndHoldGesture *tap = static_cast<const QTapAndHoldGesture*>(gesture); + formatGestureHeader(d, "QTapAndHoldGesture", tap); + d << ",position=" << tap->position() << ",timeout=" << tap->timeout() << ')'; + } + break; + case Qt::PanGesture: { + const QPanGesture *pan = static_cast<const QPanGesture*>(gesture); + formatGestureHeader(d, "QPanGesture", pan); + d << ",lastOffset=" << pan->lastOffset() << ",offset=" << pan->offset() + << ",acceleration=" << pan->acceleration() + << ",delta=" << pan->delta() << ')'; + } + break; + case Qt::PinchGesture: { + const QPinchGesture *pinch = static_cast<const QPinchGesture*>(gesture); + formatGestureHeader(d, "QPinchGesture", pinch); + d << ",totalChangeFlags=" << pinch->totalChangeFlags() + << ",changeFlags=" << pinch->changeFlags() + << ",startCenterPoint=" << pinch->startCenterPoint() + << ",lastCenterPoint=" << pinch->lastCenterPoint() + << ",centerPoint=" << pinch->centerPoint() + << ",totalScaleFactor=" << pinch->totalScaleFactor() + << ",lastScaleFactor=" << pinch->lastScaleFactor() + << ",scaleFactor=" << pinch->scaleFactor() + << ",totalRotationAngle=" << pinch->totalRotationAngle() + << ",lastRotationAngle=" << pinch->lastRotationAngle() + << ",rotationAngle=" << pinch->rotationAngle() << ')'; + } + break; + case Qt::SwipeGesture: { + const QSwipeGesture *swipe = static_cast<const QSwipeGesture*>(gesture); + formatGestureHeader(d, "QSwipeGesture", swipe); + d << ",horizontalDirection=" << swipe->horizontalDirection() + << ",verticalDirection=" << swipe->verticalDirection() + << ",swipeAngle=" << swipe->swipeAngle() << ')'; + } + break; + default: + formatGestureHeader(d, "Custom gesture", gesture); + d << ",type=" << gesture->gestureType() << ')'; + break; + } + return d; +} + +Q_WIDGETS_EXPORT QDebug operator<<(QDebug d, const QGestureEvent *gestureEvent) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "QGestureEvent(" << gestureEvent->gestures() << ')'; + return d; +} + +#endif // !QT_NO_DEBUG_STREAM QT_END_NAMESPACE diff --git a/src/widgets/kernel/qgesture.h b/src/widgets/kernel/qgesture.h index 9e3ee64c87..70b1faf735 100644 --- a/src/widgets/kernel/qgesture.h +++ b/src/widgets/kernel/qgesture.h @@ -315,6 +315,11 @@ private: friend class QGestureManager; }; +# ifndef QT_NO_DEBUG_STREAM +Q_WIDGETS_EXPORT QDebug operator<<(QDebug, const QGesture *); +Q_WIDGETS_EXPORT QDebug operator<<(QDebug, const QGestureEvent *); +# endif + QT_END_NAMESPACE Q_DECLARE_METATYPE(QGesture::GestureCancelPolicy) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 7533cacbdc..63497ce745 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1283,10 +1283,8 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) } - static int paintOnScreenEnv = -1; - if (paintOnScreenEnv == -1) - paintOnScreenEnv = qgetenv("QT_ONSCREEN_PAINT").toInt() > 0 ? 1 : 0; - if (paintOnScreenEnv == 1) + static const bool paintOnScreenEnv = qEnvironmentVariableIntValue("QT_ONSCREEN_PAINT") > 0; + if (paintOnScreenEnv) setAttribute(Qt::WA_PaintOnScreen); if (QApplicationPrivate::testAttribute(Qt::AA_NativeWindows)) @@ -2110,7 +2108,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt bool alsoNonOpaque) const { Q_Q(const QWidget); - static int disableSubtractOpaqueSiblings = qgetenv("QT_NO_SUBTRACTOPAQUESIBLINGS").toInt(); + static int disableSubtractOpaqueSiblings = qEnvironmentVariableIntValue("QT_NO_SUBTRACTOPAQUESIBLINGS"); if (disableSubtractOpaqueSiblings || q->isWindow()) return; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index bbdbabc14b..d5a91f18d6 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -612,10 +612,10 @@ public: { return p + data.wrect.topLeft(); } inline QRect mapToWS(const QRect &r) const - { QRect rr(r); rr.translate(-data.wrect.topLeft()); return rr; } + { return r.translated(-data.wrect.topLeft()); } inline QRect mapFromWS(const QRect &r) const - { QRect rr(r); rr.translate(data.wrect.topLeft()); return rr; } + { return r.translated(data.wrect.topLeft()); } QOpenGLContext *shareContext() const; diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 00052c868f..2f2db32852 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -81,14 +81,14 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion ®ion, QBack Q_ASSERT(tlw); #if !defined(QT_NO_PAINT_DEBUG) - static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt(); + static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE"); if (flushUpdate > 0) QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); #endif if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) return; - static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt(); + static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS"); if (fpsDebug) { if (!widgetBackingStore->perfFrames++) widgetBackingStore->perfTime.start(); @@ -239,12 +239,12 @@ bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn) int delay = 0; if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) { - static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt(); + static int flushPaintEvent = qEnvironmentVariableIntValue("QT_FLUSH_PAINT_EVENT"); if (!flushPaintEvent) return false; delay = flushPaintEvent; } else { - static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt(); + static int flushPaint = qEnvironmentVariableIntValue("QT_FLUSH_PAINT"); if (!flushPaint) return false; delay = flushPaint; @@ -780,10 +780,7 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) if (x->inTopLevelResize) return; - static int accelEnv = -1; - if (accelEnv == -1) { - accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0; - } + static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0; QWidget *pw = q->parentWidget(); QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); @@ -862,10 +859,7 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) if (!wbs) return; - static int accelEnv = -1; - if (accelEnv == -1) { - accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; - } + static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; QRect scrollRect = rect & clipRect(); bool overlapped = false; diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 8579308820..815b10c921 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -4923,6 +4923,25 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget ret = true; break; + case SH_Menu_SubMenuUniDirection: + ret = false; + break; + case SH_Menu_SubMenuUniDirectionFailCount: + ret = 1; + break; + case SH_Menu_SubMenuSloppySelectOtherActions: + ret = true; + break; + case SH_Menu_SubMenuSloppyCloseTimeout: + ret = 1000; + break; + case SH_Menu_SubMenuResetWhenReenteringParent: + ret = false; + break; + case SH_Menu_SubMenuDontStartSloppyOnLeave: + ret = false; + break; + case SH_ProgressDialog_TextLabelAlignment: ret = Qt::AlignCenter; break; diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index c1d6d879a8..ef3795c9a1 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -1537,7 +1537,8 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio bool ignoreCheckMark = false; int checkcol = qMax(menuItem->maxIconWidth, 20); - if (qobject_cast<const QComboBox*>(widget)) + if (qobject_cast<const QComboBox*>(widget) || + (option->styleObject && option->styleObject->property("_q_isComboBoxPopupItem").toBool())) ignoreCheckMark = true; //ignore the checkmarks provided by the QComboMenuDelegate if (!ignoreCheckMark) { @@ -1606,7 +1607,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio QRect pmr(0, 0, pixw, pixh); pmr.moveCenter(vCheckRect.center()); painter->setPen(menuItem->palette.text().color()); - if (checkable && checked) { + if (!ignoreCheckMark && checkable && checked) { QStyleOption opt = *option; if (act) { QColor activeColor = mergedColors(option->palette.background().color(), diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index d909e13f9a..fb0bafa66d 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -2819,6 +2819,19 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w case SH_Menu_SubMenuPopupDelay: ret = 100; break; + case SH_Menu_SubMenuUniDirection: + ret = true; + break; + case SH_Menu_SubMenuSloppySelectOtherActions: + ret = false; + break; + case SH_Menu_SubMenuResetWhenReenteringParent: + ret = true; + break; + case SH_Menu_SubMenuDontStartSloppyOnLeave: + ret = true; + break; + case SH_ScrollBar_LeftClickAbsolutePosition: { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; bool result = [defaults boolForKey:@"AppleScrollerPagingBehavior"]; @@ -3065,8 +3078,11 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w break; case SH_ScrollBar_Transient: if ((qobject_cast<const QScrollBar *>(w) && w->parent() && - qobject_cast<QAbstractScrollArea*>(w->parent()->parent())) || - (opt && QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ScrollBar))) { + qobject_cast<QAbstractScrollArea*>(w->parent()->parent())) +#ifndef QT_NO_ACCESSIBILITY + || (opt && QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ScrollBar)) +#endif + ) { ret = QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (ret) @@ -3712,6 +3728,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { QStyleOptionToolButton myTb = *tb; myTb.state &= ~State_AutoRaise; +#ifndef QT_NO_ACCESSIBILITY if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { QRect cr = tb->rect; int shiftX = 0; @@ -3808,6 +3825,9 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } else { QCommonStyle::drawControl(ce, &myTb, p, w); } +#else + Q_UNUSED(tb) +#endif } break; case CE_ToolBoxTabShape: @@ -5970,7 +5990,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex case CC_ToolButton: if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { - +#ifndef QT_NO_ACCESSIBILITY if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { if (tb->subControls & SC_ToolButtonMenu) { QStyleOption arrowOpt(0); @@ -6107,6 +6127,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex label.rect = buttonRect.adjusted(fw, fw, -fw, -fw); proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget); } +#endif } break; case CC_Dial: @@ -6586,7 +6607,11 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op break; case CC_ToolButton: ret = QCommonStyle::subControlRect(cc, opt, sc, widget); - if (sc == SC_ToolButtonMenu && !QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { + if (sc == SC_ToolButtonMenu +#ifndef QT_NO_ACCESSIBILITY + && !QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar) +#endif + ) { ret.adjust(-1, 0, 0, 0); } break; diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 6cf61a691e..7c4d239894 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -1707,6 +1707,32 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, other items of the menu. This is supported on most modern desktop platforms. + \value SH_Menu_SubMenuUniDirection Since Qt 5.5. If the cursor has + to move towards the submenu (like it is on OS X), or if the + cursor can move in any direction as long as it reaches the + submenu before the sloppy timeout. + + \value SH_Menu_SubMenuUniDirectionFailCount Since Qt 5.5. When + SH_Menu_SubMenuUniDirection is defined this enum defines the + number of failed mouse moves before the sloppy submenu is + discarded. This can be used to control the "strictness" of the + uni direction algorithm. + + \value SH_Menu_SubMenuSloppySelectOtherActions Since Qt 5.5. Should + other action items be selected when the mouse moves towards a + sloppy submenu. + + \value SH_Menu_SubMenuSloppyCloseTimeout Since Qt 5.5. The timeout + used to close sloppy submenus. + + \value SH_Menu_SubMenuResetWhenReenteringParent Since Qt 5.5. When + entering parent from child submenu, should the sloppy state be + reset, effectively closing the child and making the current + submenu active. + + \value SH_Menu_SubMenuDontStartSloppyOnLeave Since Qt 5.5. Do not + start sloppy timers when the mouse leaves a sub-menu. + \value SH_ScrollView_FrameOnlyAroundContents Whether scrollviews draw their frame only around contents (like Motif), or around contents, scroll bars and corner widgets (like Windows). diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index 681f3e0468..12ada3f6e4 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -701,6 +701,12 @@ public: SH_ComboBox_UseNativePopup, SH_LineEdit_PasswordMaskDelay, SH_TabBar_ChangeCurrentDelay, + SH_Menu_SubMenuUniDirection, + SH_Menu_SubMenuUniDirectionFailCount, + SH_Menu_SubMenuSloppySelectOtherActions, + SH_Menu_SubMenuSloppyCloseTimeout, + SH_Menu_SubMenuResetWhenReenteringParent, + SH_Menu_SubMenuDontStartSloppyOnLeave, // Add new style hint values here SH_CustomBase = 0xf0000000 diff --git a/src/widgets/styles/qstyleoption.cpp b/src/widgets/styles/qstyleoption.cpp index 1d98a56063..027e36387a 100644 --- a/src/widgets/styles/qstyleoption.cpp +++ b/src/widgets/styles/qstyleoption.cpp @@ -1626,6 +1626,9 @@ QStyleOptionProgressBar::QStyleOptionProgressBar(int version) \brief the progress bar's orientation (horizontal or vertical); the default orentation is Qt::Horizontal + \deprecated + Use the QStyle::State_Horizontal flag instead (in the the QStyleOption::state member). + \sa QProgressBar::orientation */ diff --git a/src/widgets/styles/qstyleoption.h b/src/widgets/styles/qstyleoption.h index a475f485df..4b55cf3faf 100644 --- a/src/widgets/styles/qstyleoption.h +++ b/src/widgets/styles/qstyleoption.h @@ -317,7 +317,7 @@ public: QString text; Qt::Alignment textAlignment; bool textVisible; - Qt::Orientation orientation; + Qt::Orientation orientation; // ### Qt 6: remove bool invertedAppearance; bool bottomToTop; diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 708014a6df..3619c3ff29 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -586,6 +586,17 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid break; } #endif // Q_OS_WIN && !Q_OS_WINRT + case SH_Menu_SubMenuSloppyCloseTimeout: + case SH_Menu_SubMenuPopupDelay: { +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) + DWORD delay; + if (SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &delay, 0)) + ret = delay; + else +#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT + ret = 400; + break; + } #ifndef QT_NO_RUBBERBAND case SH_RubberBand_Mask: if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) { diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index dfc9a8fe92..815afe85f2 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -1402,7 +1402,7 @@ bool QLineEdit::event(QEvent * e) { Q_D(QLineEdit); if (e->type() == QEvent::Timer) { - // should be timerEvent, is here for binary compatibility + // ### Qt6: move to timerEvent, is here for binary compatibility int timerId = ((QTimerEvent*)e)->timerId(); if (false) { #ifndef QT_NO_DRAGANDDROP diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 0fd645a4d3..a4c22de15b 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -70,6 +70,22 @@ QT_BEGIN_NAMESPACE QMenu *QMenuPrivate::mouseDown = 0; +QPointer<QMenu> QMenuPrivate::previousMouseMenu(Q_NULLPTR); +static void handleEnterLeaveEvents(QPointer<QMenu> *previous_ptr, QMenu *next) +{ + QWidget *previous = previous_ptr->data(); + if (previous != next) { + if (previous) { + QEvent leaveEvent(QEvent::Leave); + QApplication::sendEvent(previous, &leaveEvent); + } + if (next) { + QEvent enterEvent(QEvent::Enter); + QApplication::sendEvent(next, &enterEvent); + } + } + *previous_ptr = next; +} /* QMenu code */ // internal class used for the torn off popup @@ -148,6 +164,9 @@ void QMenuPrivate::init() } setPlatformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu()); + sloppyState.initialize(q); + delayState.initialize(q); + mousePopupDelay = q->style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, q); } void QMenuPrivate::setPlatformMenu(QPlatformMenu *menu) @@ -270,7 +289,6 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const maxIconWidth = 0; hasCheckableItems = false; ncols = 1; - sloppyAction = 0; for (int i = 0; i < actions.count(); ++i) { QAction *action = actions.at(i); @@ -482,21 +500,30 @@ void QMenuPrivate::hideMenu(QMenu *menu) aboutToHide = false; blocker.unblock(); #endif // QT_NO_EFFECTS + if (activeMenu == menu) + activeMenu = 0; + menu->d_func()->causedPopup.action = 0; + menu->d_func()->causedPopup.widget = 0; menu->close(); + if (previousMouseMenu.data() == menu) + handleEnterLeaveEvents(&previousMouseMenu, Q_NULLPTR); } void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst) { Q_Q(QMenu); - if (action && action->isEnabled()) { - if (!delay) - q->internalDelayedPopup(); - else if (!menuDelayTimer.isActive() && (!action->menu() || !action->menu()->isVisible())) - menuDelayTimer.start(delay, q); - if (activateFirst && action->menu()) - action->menu()->d_func()->setFirstActionActive(); + if (action) { + if (action->isEnabled()) { + if (!delay) + q->internalDelayedPopup(); + else if (action->menu() && !action->menu()->isVisible()) + delayState.start(delay, action); + else if (!action->menu()) + delayState.stop(); + if (activateFirst && action->menu()) + action->menu()->d_func()->setFirstActionActive(); + } } else if (QMenu *menu = activeMenu) { //hide the current item - activeMenu = 0; hideMenu(menu); } } @@ -547,33 +574,32 @@ void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason { Q_Q(QMenu); tearoffHighlighted = 0; + + if (action + && (action->isSeparator() + || (!action->isEnabled() && !q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)))) + action = Q_NULLPTR; + // Reselect the currently active action in case mouse moved over other menu items when // moving from sub menu action to sub menu (QTBUG-20094). - if (reason != SelectedFromKeyboard && action == currentAction && !(action && action->menu() && action->menu() != activeMenu)) { + if (reason != SelectedFromKeyboard) { if (QMenu *menu = qobject_cast<QMenu*>(causedPopup.widget)) { if (causedPopup.action && menu->d_func()->activeMenu == q) menu->d_func()->setCurrentAction(causedPopup.action, 0, reason, false); } - return; } if (currentAction) q->update(actionRect(currentAction)); - sloppyAction = 0; - if (!sloppyRegion.isEmpty()) - sloppyRegion = QRegion(); QMenu *hideActiveMenu = activeMenu; -#ifndef QT_NO_STATUSTIP QAction *previousAction = currentAction; -#endif currentAction = action; if (action) { if (!action->isSeparator()) { activateAction(action, QAction::Hover); if (popup != -1) { - hideActiveMenu = 0; //will be done "later" // if the menu is visible then activate the required action, // otherwise we just mark the action as currentAction // and activate it when the menu will be popuped. @@ -596,24 +622,122 @@ void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason } } } - } else { //action is a separator - if (popup != -1) - hideActiveMenu = 0; //will be done "later" } #ifndef QT_NO_STATUSTIP } else if (previousAction) { previousAction->d_func()->showStatusText(topCausedWidget(), QString()); #endif } - if (hideActiveMenu) { - activeMenu = 0; + if (hideActiveMenu && previousAction != currentAction) { + if (popup == -1) { #ifndef QT_NO_EFFECTS - // kill any running effect - qFadeEffect(0); - qScrollEffect(0); + // kill any running effect + qFadeEffect(0); + qScrollEffect(0); #endif - hideMenu(hideActiveMenu); + hideMenu(hideActiveMenu); + } else if (!currentAction || !currentAction->menu()) { + sloppyState.startTimerIfNotRunning(); + } + } +} + +void QMenuSloppyState::reset() +{ + m_enabled = false; + m_first_mouse = true; + m_init_guard = false; + m_uni_dir_discarded_count = 0; + m_time.stop(); + m_reset_action = Q_NULLPTR; + m_origin_action = Q_NULLPTR; + m_action_rect = QRect(); + m_previous_point = QPointF(); + if (m_sub_menu) { + QMenuPrivate::get(m_sub_menu)->sloppyState.m_parent = Q_NULLPTR; + m_sub_menu = Q_NULLPTR; + } +} +void QMenuSloppyState::enter() +{ + QMenuPrivate *menuPriv = QMenuPrivate::get(m_menu); + + if (m_discard_state_when_entering_parent && m_sub_menu == menuPriv->activeMenu) { + menuPriv->hideMenu(m_sub_menu); + reset(); + } + if (m_parent) + m_parent->childEnter(); +} + +void QMenuSloppyState::childLeave() +{ + if (m_enabled && !QMenuPrivate::get(m_menu)->hasReceievedEnter) { + startTimer(); + if (m_parent) + m_parent->childLeave(); + } +} + +void QMenuSloppyState::setSubMenuPopup(const QRect &actionRect, QAction *resetAction, QMenu *subMenu) +{ + m_enabled = true; + m_init_guard = true; + m_time.stop(); + m_action_rect = actionRect; + m_sub_menu = subMenu; + QMenuPrivate::get(subMenu)->sloppyState.m_parent = this; + m_reset_action = resetAction; + m_origin_action = resetAction; +} + +bool QMenuSloppyState::hasParentActiveDelayTimer() const +{ + return m_parent && m_parent->m_menu && QMenuPrivate::get(m_parent->m_menu)->delayState.timer.isActive(); +} + +class ResetOnDestroy +{ +public: + ResetOnDestroy(QMenuSloppyState *sloppyState, bool *guard) + : toReset(sloppyState) + , guard(guard) + { + *guard = false; } + + ~ResetOnDestroy() + { + if (!*guard) + toReset->reset(); + } + + QMenuSloppyState *toReset; + bool *guard; +}; + +void QMenuSloppyState::timeout() +{ + QMenuPrivate *menu_priv = QMenuPrivate::get(m_menu); + if (menu_priv->currentAction == m_reset_action + && menu_priv->hasReceievedEnter + && (menu_priv->currentAction + && menu_priv->currentAction->menu() == menu_priv->activeMenu)) { + return; + } + + ResetOnDestroy resetState(this, &m_init_guard); + + if (hasParentActiveDelayTimer() || !m_menu || !m_menu->isVisible()) + return; + + if (m_sub_menu) + menu_priv->hideMenu(m_sub_menu); + + if (menu_priv->hasReceievedEnter) + menu_priv->setCurrentAction(m_reset_action,0); + else + menu_priv->setCurrentAction(Q_NULLPTR, 0); } //return the top causedPopup.widget that is not a QMenu @@ -964,8 +1088,10 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) tearoffHighlighted = 0; } - if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it.. + if (q->frameGeometry().contains(e->globalPos())) { //otherwise if the event is in our rect we want it.. + handleEnterLeaveEvents(&previousMouseMenu, q); return false; + } for(QWidget *caused = causedPopup.widget; caused;) { bool passOnEvent = false; @@ -981,16 +1107,17 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) next_widget = m->d_func()->causedPopup.widget; } if (passOnEvent) { + handleEnterLeaveEvents(&previousMouseMenu,qobject_cast<QMenu *>(caused)); if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) { QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->screenPos(), e->button(), e->buttons(), e->modifiers()); QApplication::sendEvent(caused, &new_e); return true; + } } - } - if (!next_widget) - break; caused = next_widget; + if (!caused) + handleEnterLeaveEvents(&previousMouseMenu, Q_NULLPTR); } return false; } @@ -2235,6 +2362,8 @@ void QMenu::hideEvent(QHideEvent *) #endif d->mouseDown = 0; d->hasHadMouse = false; + if (d->activeMenu) + d->hideMenu(d->activeMenu); d->causedPopup.widget = 0; d->causedPopup.action = 0; if (d->scroll) @@ -2401,7 +2530,7 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) #endif d->activateAction(action, QAction::Trigger); } - } else if (d->hasMouseMoved(e->globalPos())) { + } else if ((!action || action->isEnabled()) && d->hasMouseMoved(e->globalPos())) { d->hideUpToMenuBar(); } } @@ -2466,8 +2595,8 @@ QMenu::event(QEvent *e) } } break; case QEvent::ContextMenu: - if(d->menuDelayTimer.isActive()) { - d->menuDelayTimer.stop(); + if (d->delayState.timer.isActive()) { + d->delayState.stop(); internalDelayedPopup(); } break; @@ -2484,6 +2613,7 @@ QMenu::event(QEvent *e) case QEvent::Show: d->mouseDown = 0; d->updateActionRects(); + d->sloppyState.reset(); if (d->currentAction) d->popupAction(d->currentAction, 0, false); break; @@ -2891,34 +3021,34 @@ void QMenu::mouseMoveEvent(QMouseEvent *e) Q_D(QMenu); if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e)) return; + d->motions++; - if (d->motions == 0) // ignore first mouse move event (see enterEvent()) + if (d->motions == 0) return; + d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos()); QAction *action = d->actionAt(e->pos()); - if (!action || action->isSeparator()) { + if ((!action || action->isSeparator()) && !d->sloppyState.enabled()) { if (d->hasHadMouse - && d->sloppyDelayTimer == 0 // Keep things as they are while we're moving to the submenu - && (!d->currentAction || (action && action->isSeparator()) - || !(d->currentAction->menu() && d->currentAction->menu()->isVisible()))) - d->setCurrentAction(0); + || (!d->currentAction || !d->currentAction->menu() || !d->currentAction->menu()->isVisible())) { + d->setCurrentAction(action); + } return; - } else if(e->buttons()) { - d->mouseDown = this; } - if (d->sloppyRegion.contains(e->pos())) { - // If the timer is already running then don't start a new one unless the action is the same - if (d->sloppyAction != action && d->sloppyDelayTimer != 0) { - killTimer(d->sloppyDelayTimer); - d->sloppyDelayTimer = 0; - } - if (d->sloppyDelayTimer == 0) { - d->sloppyAction = action; - d->sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this) * 6); - } - } else if (action != d->currentAction) { - d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)); + + if (e->buttons()) + d->mouseDown = this; + + if (d->activeMenu) + d->activeMenu->d_func()->setCurrentAction(0); + + QMenuSloppyState::MouseEventResult sloppyEventResult = d->sloppyState.processMouseEvent(e->localPos(), action, d->currentAction); + if (sloppyEventResult == QMenuSloppyState::EventShouldBePropogated) { + d->setCurrentAction(action, d->mousePopupDelay); + } else if (sloppyEventResult == QMenuSloppyState::EventDiscardsSloppyState) { + d->sloppyState.reset(); + d->hideMenu(d->activeMenu); } } @@ -2927,7 +3057,11 @@ void QMenu::mouseMoveEvent(QMouseEvent *e) */ void QMenu::enterEvent(QEvent *) { - d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent() + Q_D(QMenu); + d->hasReceievedEnter = true; + d->sloppyState.enter(); + d->sloppyState.startTimer(); + d->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent() } /*! @@ -2936,9 +3070,8 @@ void QMenu::enterEvent(QEvent *) void QMenu::leaveEvent(QEvent *) { Q_D(QMenu); - d->sloppyAction = 0; - if (!d->sloppyRegion.isEmpty()) - d->sloppyRegion = QRegion(); + d->hasReceievedEnter = false; + d->sloppyState.leave(); if (!d->activeMenu && d->currentAction) setActiveAction(0); } @@ -2954,13 +3087,14 @@ QMenu::timerEvent(QTimerEvent *e) d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection); if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone) d->scroll->scrollTimer.stop(); - } else if(d->menuDelayTimer.timerId() == e->timerId()) { - d->menuDelayTimer.stop(); + } else if (d->delayState.timer.timerId() == e->timerId()) { + if (d->currentAction && !d->currentAction->menu()) + return; + d->delayState.stop(); + d->sloppyState.stopTimer(); internalDelayedPopup(); - } else if (d->sloppyDelayTimer == e->timerId()) { - killTimer(d->sloppyDelayTimer); - d->sloppyDelayTimer = 0; - internalSetSloppyAction(); + } else if (d->sloppyState.isTimerId(e->timerId())) { + d->sloppyState.timeout(); } else if(d->searchBufferTimer.timerId() == e->timerId()) { d->searchBuffer.clear(); } @@ -3083,23 +3217,13 @@ void QMenu::actionEvent(QActionEvent *e) /*! \internal */ -void QMenu::internalSetSloppyAction() -{ - if (d_func()->sloppyAction) - d_func()->setCurrentAction(d_func()->sloppyAction, 0); -} - -/*! - \internal -*/ void QMenu::internalDelayedPopup() { Q_D(QMenu); - //hide the current item if (QMenu *menu = d->activeMenu) { - d->activeMenu = 0; - d->hideMenu(menu); + if (d->activeMenu->menuAction() != d->currentAction) + d->hideMenu(menu); } if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() || @@ -3113,34 +3237,12 @@ void QMenu::internalDelayedPopup() int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this); const QRect actionRect(d->actionRect(d->currentAction)); - const QSize menuSize(d->activeMenu->sizeHint()); const QPoint rightPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top()))); QPoint pos(rightPos); - //calc sloppy focus buffer - if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) { - QPoint cur = QCursor::pos(); - if (actionRect.contains(mapFromGlobal(cur))) { - QPoint pts[4]; - pts[0] = QPoint(cur.x(), cur.y() - 2); - pts[3] = QPoint(cur.x(), cur.y() + 2); - if (pos.x() >= cur.x()) { - pts[1] = QPoint(geometry().right(), pos.y()); - pts[2] = QPoint(geometry().right(), pos.y() + menuSize.height()); - } else { - pts[1] = QPoint(pos.x() + menuSize.width(), pos.y()); - pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height()); - } - QPolygon points(4); - for(int i = 0; i < 4; i++) - points.setPoint(i, mapFromGlobal(pts[i])); - d->sloppyRegion = QRegion(points); - } - } - - //do the popup d->activeMenu->popup(pos); + d->sloppyState.setSubMenuPopup(actionRect, d->currentAction, d->activeMenu); } /*! diff --git a/src/widgets/widgets/qmenu.h b/src/widgets/widgets/qmenu.h index 937f6e3316..0fcbb3d29b 100644 --- a/src/widgets/widgets/qmenu.h +++ b/src/widgets/widgets/qmenu.h @@ -180,7 +180,6 @@ protected: #endif private Q_SLOTS: - void internalSetSloppyAction(); void internalDelayedPopup(); private: diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index 4a4518671c..64a728eaba 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -71,20 +71,230 @@ struct QWceMenuAction { }; #endif +template <typename T> +class QSetValueOnDestroy +{ +public: + QSetValueOnDestroy(T &toSet, T value) + : toSet(toSet) + , value(value) + { } + + ~QSetValueOnDestroy() { toSet = value; } +private: + T &toSet; + T value; +}; + +class QMenuSloppyState +{ + Q_DISABLE_COPY(QMenuSloppyState) +public: + QMenuSloppyState() + : m_menu(Q_NULLPTR) + , m_enabled(false) + , m_uni_directional(false) + , m_select_other_actions(false) + , m_first_mouse(true) + , m_init_guard(false) + , m_uni_dir_discarded_count(0) + , m_uni_dir_fail_at_count(0) + , m_timeout(0) + , m_reset_action(Q_NULLPTR) + , m_origin_action(Q_NULLPTR) + , m_parent(Q_NULLPTR) + { } + + ~QMenuSloppyState() { reset(); } + + void initialize(QMenu *menu) + { + m_menu = menu; + m_uni_directional = menu->style()->styleHint(QStyle::SH_Menu_SubMenuUniDirection, 0, menu); + m_uni_dir_fail_at_count = menu->style()->styleHint(QStyle::SH_Menu_SubMenuUniDirectionFailCount, 0, menu); + m_select_other_actions = menu->style()->styleHint(QStyle::SH_Menu_SubMenuSloppySelectOtherActions, 0 , menu); + m_timeout = menu->style()->styleHint(QStyle::SH_Menu_SubMenuSloppyCloseTimeout); + m_discard_state_when_entering_parent = menu->style()->styleHint(QStyle::SH_Menu_SubMenuResetWhenReenteringParent); + m_dont_start_time_on_leave = menu->style()->styleHint(QStyle::SH_Menu_SubMenuDontStartSloppyOnLeave); + reset(); + } + + void reset(); + bool enabled() const { return m_enabled; } + + void setResetAction(QAction *action) { m_reset_action = action; } + + enum MouseEventResult { + EventIsProcessed, + EventShouldBePropogated, + EventDiscardsSloppyState + }; + + void startTimer() + { + if (m_enabled) + m_time.start(m_timeout, m_menu); + } + + void startTimerIfNotRunning() + { + if (!m_time.isActive()) + startTimer(); + } + + void stopTimer() + { + m_time.stop(); + } + + void enter(); + + void childEnter() + { + stopTimer(); + if (m_parent) + m_parent->childEnter(); + } + + void leave() + { + if (m_dont_start_time_on_leave) + return; + if (m_parent) + m_parent->childLeave(); + startTimer(); + } + void childLeave(); + + static float slope(const QPointF &p1, const QPointF &p2) + { + const QPointF slope = p2 - p1; + if (slope.x()== 0) + return 9999; + return slope.y()/slope.x(); + } + + bool checkSlope(qreal oldS, qreal newS, bool wantSteeper) + { + if (wantSteeper) + return oldS <= newS; + return newS <= oldS; + } + + MouseEventResult processMouseEvent(const QPointF &mousePos, QAction *resetAction, QAction *currentAction) + { + if (m_parent) + m_parent->stopTimer(); + + if (!m_enabled) + return EventShouldBePropogated; + + if (!m_time.isActive()) + startTimer(); + + if (!m_sub_menu) { + reset(); + return EventShouldBePropogated; + } + + QSetValueOnDestroy<bool> setFirstMouse(m_first_mouse, false); + QSetValueOnDestroy<QPointF> setPreviousPoint(m_previous_point, mousePos); + + if (resetAction && resetAction->isSeparator()) + m_reset_action = Q_NULLPTR; + else { + m_reset_action = resetAction; + } + + if (m_action_rect.contains(mousePos)) { + startTimer(); + return currentAction == m_menu->menuAction() ? EventIsProcessed : EventShouldBePropogated; + } + + if (m_uni_directional && !m_first_mouse && resetAction != m_origin_action) { + bool left_to_right = m_menu->layoutDirection() == Qt::LeftToRight; + QRect sub_menu_rect = m_sub_menu->geometry(); + QPoint sub_menu_top = + left_to_right? sub_menu_rect.topLeft() : sub_menu_rect.topRight(); + QPoint sub_menu_bottom = + left_to_right? sub_menu_rect.bottomLeft() : sub_menu_rect.bottomRight(); + qreal prev_slope_top = slope(m_previous_point, sub_menu_top); + qreal prev_slope_bottom = slope(m_previous_point, sub_menu_bottom); + + qreal current_slope_top = slope(mousePos, sub_menu_top); + qreal current_slope_bottom = slope(mousePos, sub_menu_bottom); + + bool slopeTop = checkSlope(prev_slope_top, current_slope_top, sub_menu_top.y() < mousePos.y()); + bool slopeBottom = checkSlope(prev_slope_bottom, current_slope_bottom, sub_menu_bottom.y() > mousePos.y()); + bool rightDirection = false; + int mouseDir = m_previous_point.y() - mousePos.y(); + if (mouseDir >= 0) { + rightDirection = rightDirection || slopeTop; + } + if (mouseDir <= 0) { + rightDirection = rightDirection || slopeBottom; + } + + if (m_uni_dir_discarded_count >= m_uni_dir_fail_at_count && !rightDirection) { + m_uni_dir_discarded_count = 0; + return EventDiscardsSloppyState; + } + + if (!rightDirection) + m_uni_dir_discarded_count++; + else + m_uni_dir_discarded_count = 0; + + } + + return m_select_other_actions ? EventShouldBePropogated : EventIsProcessed; + } + + void setSubMenuPopup(const QRect &actionRect, QAction *resetAction, QMenu *subMenu); + bool hasParentActiveDelayTimer() const; + void timeout(); + int timeForTimeout() const { return m_timeout; } + + bool isTimerId(int timerId) const { return m_time.timerId() == timerId; } + QMenu *subMenu() const { return m_sub_menu; } + +private: + QMenu *m_menu; + bool m_enabled; + bool m_uni_directional; + bool m_select_other_actions; + bool m_first_mouse; + bool m_init_guard; + bool m_discard_state_when_entering_parent; + bool m_dont_start_time_on_leave; + short m_uni_dir_discarded_count; + short m_uni_dir_fail_at_count; + short m_timeout; + QBasicTimer m_time; + QAction *m_reset_action; + QAction *m_origin_action; + QRectF m_action_rect; + QPointF m_previous_point; + QPointer<QMenu> m_sub_menu; + QMenuSloppyState *m_parent; +}; + class QMenuPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QMenu) public: QMenuPrivate() : itemsDirty(0), maxIconWidth(0), tabWidth(0), ncols(0), collapsibleSeparators(true), toolTipsVisible(false), - activationRecursionGuard(false), hasHadMouse(0), aboutToHide(0), motions(0), + activationRecursionGuard(false), delayedPopupGuard(false), + hasReceievedEnter(false), + hasHadMouse(0), aboutToHide(0), motions(0), currentAction(0), #ifdef QT_KEYPAD_NAVIGATION selectAction(0), cancelAction(0), #endif scroll(0), eventLoop(0), tearoff(0), tornoff(0), tearoffHighlighted(0), - hasCheckableItems(0), sloppyDelayTimer(0), sloppyAction(0), doChildEffects(false), platformMenu(0) + hasCheckableItems(0), doChildEffects(false), platformMenu(0) #if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) ,wce_menu(0) @@ -127,6 +337,8 @@ public: int getLastVisibleAction() const; bool activationRecursionGuard; + bool delayedPopupGuard; + bool hasReceievedEnter; //selection static QMenu *mouseDown; @@ -134,12 +346,39 @@ public: uint hasHadMouse : 1; uint aboutToHide : 1; int motions; + int mousePopupDelay; QAction *currentAction; #ifdef QT_KEYPAD_NAVIGATION QAction *selectAction; QAction *cancelAction; #endif - QBasicTimer menuDelayTimer; + struct DelayState { + DelayState() + : parent(0) + , action(0) + { } + void initialize(QMenu *parent) + { + this->parent = parent; + } + + void start(int timeout, QAction *toStartAction) + { + if (timer.isActive() && toStartAction == action) + return; + action = toStartAction; + timer.start(timeout,parent); + } + void stop() + { + action = 0; + timer.stop(); + } + + QMenu *parent; + QBasicTimer timer; + QAction *action; + } delayState; enum SelectionReason { SelectedFromKeyboard, SelectedFromElsewhere @@ -198,10 +437,7 @@ public: mutable bool hasCheckableItems; - //sloppy selection - int sloppyDelayTimer; - mutable QAction *sloppyAction; - QRegion sloppyRegion; + QMenuSloppyState sloppyState; //default action QPointer<QAction> defaultAction; @@ -257,6 +493,7 @@ public: QAction* wceCommands(uint command); #endif QPointer<QWidget> noReplayFor; + static QPointer<QMenu> previousMouseMenu; }; #endif // QT_NO_MENU diff --git a/src/widgets/widgets/qprogressbar.cpp b/src/widgets/widgets/qprogressbar.cpp index 5501247973..751fcda871 100644 --- a/src/widgets/widgets/qprogressbar.cpp +++ b/src/widgets/widgets/qprogressbar.cpp @@ -132,7 +132,7 @@ void QProgressBar::initStyleOption(QStyleOptionProgressBar *option) const if (QStyleOptionProgressBarV2 *optionV2 = qstyleoption_cast<QStyleOptionProgressBarV2 *>(option)) { - optionV2->orientation = d->orientation; // ### Qt 5: use State_Horizontal instead + optionV2->orientation = d->orientation; // ### Qt 6: remove this member from QStyleOptionProgressBarV2 optionV2->invertedAppearance = d->invertedAppearance; optionV2->bottomToTop = (d->textDirection == QProgressBar::BottomToTop); } |