summaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
authorDongmei Wang <dongmei.wang@qt.io>2016-10-04 13:05:15 -0700
committerDongmei Wang <dongmei.wang@qt.io>2016-10-27 05:28:09 +0000
commitc8c3d39e2116251133fb3ee25b615a1522685861 (patch)
treef0c2f1e9c798efa86bac1a20d400a60b08fde740 /src/widgets
parent7f0d43fd332519e2689ffbff8198590d768864bc (diff)
Paint menu scrollers, tear off on top of QWidgetAction items
The menu scrollers and the tear off items are painted in the layer of QMenu. In a case that QMenu contains QWidgetAction items, the items are painted under QWidgetAction items. They are visually hidden unless QWidgetAction items' background is transparent. The tear off doesn't work since QWidgetAction item on top of it grabs mouse events. To fix the issue, add two child widgets in QMenu, paint the scroll up and the tear off items in one child widget and the scroll down item in the other one. Both child widgets are painted on top of overlapping sibling menu item widgets. Change-Id: I7eaef21b667b388fbb78b60664f4a4bd91215309 Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/widgets/qmenu.cpp139
-rw-r--r--src/widgets/widgets/qmenu_p.h21
2 files changed, 130 insertions, 30 deletions
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index b0c9512344..36a8a96b79 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -816,6 +816,93 @@ void QMenuPrivate::updateLayoutDirection()
}
}
+void QMenuPrivate::drawScroller(QPainter *painter, QMenuPrivate::ScrollerTearOffItem::Type type, const QRect &rect)
+{
+ if (!painter || rect.isEmpty())
+ return;
+
+ if (!scroll || !(scroll->scrollFlags & (QMenuPrivate::QMenuScroller::ScrollUp
+ | QMenuPrivate::QMenuScroller::ScrollDown)))
+ return;
+
+ Q_Q(QMenu);
+ QStyleOptionMenuItem menuOpt;
+ menuOpt.initFrom(q);
+ menuOpt.state = QStyle::State_None;
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.maxIconWidth = 0;
+ menuOpt.tabWidth = 0;
+ menuOpt.rect = rect;
+ menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
+ menuOpt.state |= QStyle::State_Enabled;
+ if (type == QMenuPrivate::ScrollerTearOffItem::ScrollDown)
+ menuOpt.state |= QStyle::State_DownArrow;
+
+ painter->setClipRect(menuOpt.rect);
+ q->style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, painter, q);
+}
+
+void QMenuPrivate::drawTearOff(QPainter *painter, const QRect &rect)
+{
+ if (!painter || rect.isEmpty())
+ return;
+
+ if (!tearoff)
+ return;
+
+ Q_Q(QMenu);
+ QStyleOptionMenuItem menuOpt;
+ menuOpt.initFrom(q);
+ menuOpt.state = QStyle::State_None;
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.maxIconWidth = 0;
+ menuOpt.tabWidth = 0;
+ menuOpt.rect = rect;
+ menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
+ if (tearoffHighlighted)
+ menuOpt.state |= QStyle::State_Selected;
+
+ painter->setClipRect(menuOpt.rect);
+ q->style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, painter, q);
+}
+
+QMenuPrivate::ScrollerTearOffItem::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::Type type, QMenuPrivate *mPrivate, QWidget *parent, Qt::WindowFlags f)
+ : QWidget(parent, f), menuPrivate(mPrivate), scrollType(type)
+{
+ if (parent)
+ setMouseTracking(parent->style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, parent));
+}
+
+void QMenuPrivate::ScrollerTearOffItem::paintEvent(QPaintEvent *e)
+{
+ if (!e->rect().intersects(rect()))
+ return;
+
+ QPainter p(this);
+ QWidget *parent = parentWidget();
+
+ //paint scroll up / down arrows
+ menuPrivate->drawScroller(&p, scrollType, QRect(0, 0, width(), menuPrivate->scrollerHeight()));
+ //paint the tear off
+ if (scrollType == QMenuPrivate::ScrollerTearOffItem::ScrollUp) {
+ QRect rect(0, 0, width(), parent->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, parent));
+ if (menuPrivate->scroll && menuPrivate->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
+ rect.translate(0, menuPrivate->scrollerHeight());
+ menuPrivate->drawTearOff(&p, rect);
+ }
+}
+
+void QMenuPrivate::ScrollerTearOffItem::updateScrollerRects(const QRect &rect)
+{
+ if (rect.isEmpty())
+ setVisible(false);
+ else {
+ setGeometry(rect);
+ raise();
+ setVisible(true);
+ }
+}
+
/*!
Returns the action associated with this menu.
@@ -2595,35 +2682,20 @@ void QMenu::paintEvent(QPaintEvent *e)
style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
}
- if (!scrollUpRect.isEmpty()) {
- menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
- menuOpt.state |= QStyle::State_Enabled;
- menuOpt.rect = scrollUpRect;
- emptyArea -= QRegion(menuOpt.rect);
- p.setClipRect(menuOpt.rect);
- style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
- }
-
- if (!scrollDownRect.isEmpty()) {
- menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
- menuOpt.state |= QStyle::State_Enabled;
- menuOpt.state |= QStyle::State_DownArrow;
- menuOpt.rect = scrollDownRect;
- emptyArea -= QRegion(menuOpt.rect);
- p.setClipRect(menuOpt.rect);
- style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
- }
+ emptyArea -= QRegion(scrollUpTearOffRect);
+ emptyArea -= QRegion(scrollDownRect);
- //paint the tear off..
- if (!tearOffRect.isEmpty()) {
- menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
- menuOpt.rect = tearOffRect;
- emptyArea -= QRegion(menuOpt.rect);
- p.setClipRect(menuOpt.rect);
- menuOpt.state = QStyle::State_None;
- if (d->tearoffHighlighted)
- menuOpt.state |= QStyle::State_Selected;
- style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
+ if (d->scrollUpTearOffItem || d->scrollDownItem) {
+ if (d->scrollUpTearOffItem)
+ d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
+ if (d->scrollDownItem)
+ d->scrollDownItem->updateScrollerRects(scrollDownRect);
+ } else {
+ //paint scroll up /down
+ d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
+ d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
+ //paint the tear off..
+ d->drawTearOff(&p, tearOffRect);
}
//draw border
@@ -3356,8 +3428,17 @@ void QMenu::actionEvent(QActionEvent *e)
}
if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
QWidget *widget = wa->requestWidget(this);
- if (widget)
+ if (widget) {
d->widgetItems.insert(wa, widget);
+ if (d->scroll) {
+ if (!d->scrollUpTearOffItem)
+ d->scrollUpTearOffItem =
+ new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d, this);
+ if (!d->scrollDownItem)
+ d->scrollDownItem =
+ new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d, this);
+ }
+ }
}
} else if (e->type() == QEvent::ActionRemoved) {
e->action()->disconnect(this);
diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h
index 3166e6f6cd..64291e842f 100644
--- a/src/widgets/widgets/qmenu_p.h
+++ b/src/widgets/widgets/qmenu_p.h
@@ -276,7 +276,8 @@ public:
cancelAction(0),
#endif
scroll(0), eventLoop(0), tearoff(0), tornoff(0), tearoffHighlighted(0),
- hasCheckableItems(0), doChildEffects(false), platformMenu(0)
+ hasCheckableItems(0), doChildEffects(false), platformMenu(0),
+ scrollUpTearOffItem(nullptr), scrollDownItem(nullptr)
{ }
~QMenuPrivate()
@@ -445,6 +446,24 @@ public:
QPointer<QAction> actionAboutToTrigger;
QPointer<QWidget> noReplayFor;
+
+ class ScrollerTearOffItem : public QWidget {
+ public:
+ enum Type { ScrollUp, ScrollDown };
+ ScrollerTearOffItem(Type type, QMenuPrivate *mPrivate,
+ QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
+ void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE;
+ void updateScrollerRects(const QRect &rect);
+
+ private:
+ QMenuPrivate *menuPrivate;
+ Type scrollType;
+ };
+ ScrollerTearOffItem *scrollUpTearOffItem;
+ ScrollerTearOffItem *scrollDownItem;
+
+ void drawScroller(QPainter *painter, ScrollerTearOffItem::Type type, const QRect &rect);
+ void drawTearOff(QPainter *painter, const QRect &rect);
};
#endif // QT_NO_MENU