From 063997f44ffc1b6650ef6d67832674d5511fdb63 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 14 Oct 2016 16:16:02 +0200 Subject: QMenu: don't force platform instance creation on construction There's a conflict between QGtk3Menu and QDbusPlatformMenuBar. The problem is that on Unity the type of the platform menu instance must be different depending on whether the menu is in the global menubar or a standalone context menu. Since QMenu creates a platform menu instance at construction time, it does not yet know whether it will be added into a menubar. QMenuBar checks that the QMenu already has a platform menu instance, and passes it to the platform menubar. As a result, a QGtk3Menu instance is passed to QDbusPlatformMenuBar. Currently, a standalone QMenu does not use the native platform menu instance. Only menus that are added to a QMenuBar do. Therefore we don't need to create the platform instance when QMenu is constructed, but only after it is added to QMenuBar. The platform menu instance creation is implemented in QMenuBarPrivate::getPlatformMenu(), and QMenu::setPlatformMenu() calls syncPlatformMenu() to take care of syncing the QMenu properties and actions to the new platform menu instance. The macOS-specific methods QMenu::toNSMenu() and QMenu::setAsDockMenu() rely on the platform menu instance, and must therefore create it on demand. This is a hot fix for the release blocker, not a long term solution. In the future, if standalone QMenus are made to use native platform menu instances, the instance must be created lazily when the menu is about to be made visible. Task-number: QTBUG-56526 Change-Id: I044933cabb1639406fe47908dfc4b1903af214d1 Reviewed-by: Dmitry Shachnev Reviewed-by: Lars Knoll --- src/widgets/widgets/qmenu.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/widgets/widgets/qmenu.cpp') diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index d957dda162..a983dc6fde 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -153,12 +153,19 @@ void QMenuPrivate::init() 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); -- cgit v1.2.3 From 7f0d43fd332519e2689ffbff8198590d768864bc Mon Sep 17 00:00:00 2001 From: Dongmei Wang Date: Fri, 30 Sep 2016 12:01:02 -0700 Subject: Don't overlap the menu scrollers with QAction items The QAction items overlap with the menu scrollers. The patch is to set correct clip regions for QAction items in order not to overlap with the scroll arrows. Change-Id: Ie724bd2be522daf5935fb489523b5d5f32bb7f71 Reviewed-by: Gabriel de Dietrich --- src/widgets/widgets/qmenu.cpp | 89 ++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 27 deletions(-) (limited to 'src/widgets/widgets/qmenu.cpp') diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index a983dc6fde..b0c9512344 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -2539,51 +2539,85 @@ void QMenu::paintEvent(QPaintEvent *e) menuOpt.tabWidth = 0; style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this); + //calculate the scroll up / down rect + const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this); + QRect scrollUpRect, scrollDownRect; + if (d->scroll) { + if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) + scrollUpRect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight()); + + if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) + scrollDownRect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2), + d->scrollerHeight()); + } + + //calculate the tear off rect + QRect tearOffRect; + if (d->tearoff) { + tearOffRect.setRect(fw, fw, width() - (fw * 2), + style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this)); + if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) + tearOffRect.translate(0, d->scrollerHeight()); + } + //draw the items that need updating.. + QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect); for (int i = 0; i < d->actions.count(); ++i) { QAction *action = d->actions.at(i); - QRect adjustedActionRect = d->actionRects.at(i); - if (!e->rect().intersects(adjustedActionRect) + QRect actionRect = d->actionRects.at(i); + if (!e->rect().intersects(actionRect) || d->widgetItems.value(action)) continue; //set the clip region to be extra safe (and adjust for the scrollers) + emptyArea -= QRegion(actionRect); + + QRect adjustedActionRect = actionRect; + if (adjustedActionRect.intersects(scrollUpTearOffRect)) { + if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom()) + continue; + else + adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1); + } + + if (adjustedActionRect.intersects(scrollDownRect)) { + if (adjustedActionRect.top() >= scrollDownRect.top()) + continue; + else + adjustedActionRect.setBottom(scrollDownRect.top()-1); + } + QRegion adjustedActionReg(adjustedActionRect); - emptyArea -= adjustedActionReg; p.setClipRegion(adjustedActionReg); QStyleOptionMenuItem opt; initStyleOption(&opt, action); - opt.rect = adjustedActionRect; + opt.rect = actionRect; style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this); } - const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this); - //draw the scroller regions.. - if (d->scroll) { + if (!scrollUpRect.isEmpty()) { menuOpt.menuItemType = QStyleOptionMenuItem::Scroller; menuOpt.state |= QStyle::State_Enabled; - if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) { - menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight()); - emptyArea -= QRegion(menuOpt.rect); - p.setClipRect(menuOpt.rect); - style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this); - } - if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) { - menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2), - d->scrollerHeight()); - emptyArea -= QRegion(menuOpt.rect); - menuOpt.state |= QStyle::State_DownArrow; - p.setClipRect(menuOpt.rect); - style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this); - } + 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); } + //paint the tear off.. - if (d->tearoff) { + if (!tearOffRect.isEmpty()) { menuOpt.menuItemType = QStyleOptionMenuItem::TearOff; - menuOpt.rect.setRect(fw, fw, width() - (fw * 2), - style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this)); - if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) - menuOpt.rect.translate(0, d->scrollerHeight()); + menuOpt.rect = tearOffRect; emptyArea -= QRegion(menuOpt.rect); p.setClipRect(menuOpt.rect); menuOpt.state = QStyle::State_None; @@ -2591,6 +2625,7 @@ void QMenu::paintEvent(QPaintEvent *e) menuOpt.state |= QStyle::State_Selected; style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this); } + //draw border if (fw) { QRegion borderReg; @@ -2609,7 +2644,7 @@ void QMenu::paintEvent(QPaintEvent *e) style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this); } - //finally the rest of the space + //finally the rest of the spaces p.setClipRegion(emptyArea); menuOpt.state = QStyle::State_None; menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea; -- cgit v1.2.3 From c8c3d39e2116251133fb3ee25b615a1522685861 Mon Sep 17 00:00:00 2001 From: Dongmei Wang Date: Tue, 4 Oct 2016 13:05:15 -0700 Subject: 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 --- src/widgets/widgets/qmenu.cpp | 139 +++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 29 deletions(-) (limited to 'src/widgets/widgets/qmenu.cpp') 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(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); -- cgit v1.2.3