summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/widgets/qmenu.cpp78
-rw-r--r--src/widgets/widgets/qmenu_p.h1
-rw-r--r--tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp185
3 files changed, 242 insertions, 22 deletions
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index 2b10ae7261..f08f573391 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -267,9 +267,6 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const
int lastVisibleAction = getLastVisibleAction();
- int max_column_width = 0,
- dh = screen.height(),
- y = 0;
QStyle *style = q->style();
QStyleOption opt;
opt.init(q);
@@ -279,6 +276,10 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const
const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
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;
+ int max_column_width = 0;
+ int dh = screen.height();
+ int y = base_y;
//for compatibility now - will have to refactor this away
tabWidth = 0;
@@ -356,11 +357,12 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const
max_column_width = qMax(max_column_width, sz.width());
//wrapping
if (!scroll &&
- y+sz.height()+vmargin > dh - (deskFw * 2)) {
+ y + sz.height() + vmargin + bottommargin + fw > dh - (deskFw * 2)) {
ncols++;
- y = vmargin;
+ y = base_y;
+ } else {
+ y += sz.height();
}
- y += sz.height();
//update the item
actionRects[i] = QRect(0, 0, sz.width(), sz.height());
}
@@ -372,9 +374,6 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const
max_column_width = qMax(min_column_width, max_column_width);
//calculate position
- const int base_y = vmargin + fw + topmargin +
- (scroll ? scroll->scrollOffset : 0) +
- tearoffHeight;
int x = hmargin + fw + leftmargin;
y = base_y;
@@ -383,7 +382,7 @@ void QMenuPrivate::updateActionRects(const QRect &screen) const
if (rect.isNull())
continue;
if (!scroll &&
- y+rect.height() > dh - deskFw * 2) {
+ y + rect.height() + vmargin + bottommargin + fw > dh - deskFw * 2) {
x += max_column_width + hmargin;
y = base_y;
}
@@ -761,7 +760,7 @@ QWidget *QMenuPrivate::topCausedWidget() const
QAction *QMenuPrivate::actionAt(QPoint p) const
{
- if (!q_func()->rect().contains(p)) //sanity check
+ if (!rect().contains(p)) //sanity check
return 0;
for(int i = 0; i < actionRects.count(); i++) {
@@ -855,6 +854,19 @@ void QMenuPrivate::drawTearOff(QPainter *painter, const QRect &rect)
q->style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, painter, q);
}
+QRect QMenuPrivate::rect() const
+{
+ Q_Q(const QMenu);
+ QStyle *style = q->style();
+ QStyleOption opt(0);
+ opt.init(q);
+ const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q);
+ 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)));
+}
+
QMenuPrivate::ScrollerTearOffItem::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::Type type, QMenuPrivate *mPrivate, QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f), menuPrivate(mPrivate), scrollType(type)
{
@@ -989,7 +1001,9 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc
}
if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) {
- newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin; //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);
}
if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) {
@@ -1141,15 +1155,23 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
{
Q_Q(QMenu);
QPoint pos = q->mapFromGlobal(e->globalPos());
+
+ QStyle *style = q->style();
+ QStyleOption opt(0);
+ opt.init(q);
+ const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q);
+ const int vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q);
+ const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
+
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());
+ isScroll = (pos.y() <= scrollerHeight() + fw + vmargin + topmargin);
else if (dir == QMenuScroller::ScrollDown)
- isScroll = (pos.y() >= q->height() - scrollerHeight());
+ isScroll = (pos.y() >= q->height() - scrollerHeight() - fw - vmargin - bottommargin);
if (isScroll) {
scroll->scrollDirection = dir;
break;
@@ -1166,7 +1188,8 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
}
if (tearoff) { //let the tear off thingie "steal" the event..
- QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, 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);
@@ -2615,21 +2638,28 @@ 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 vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin, 0, this);
+
QRect scrollUpRect, scrollDownRect;
+ const int leftmargin = fw + hmargin + d->leftmargin;
+ const int topmargin = fw + vmargin + d->topmargin;
+ const int bottommargin = fw + vmargin + d->bottommargin;
+ const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
if (d->scroll) {
if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
- scrollUpRect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight());
+ scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
- scrollDownRect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2),
- d->scrollerHeight());
+ scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
+ contentWidth, 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));
+ tearOffRect.setRect(leftmargin, topmargin, contentWidth,
+ style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
tearOffRect.translate(0, d->scrollerHeight());
}
@@ -2646,6 +2676,12 @@ void QMenu::paintEvent(QPaintEvent *e)
emptyArea -= QRegion(actionRect);
QRect adjustedActionRect = actionRect;
+ if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
+ continue;
+
+ if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
+ continue;
+
if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
continue;
diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h
index 898fa3161b..a160a2c7d4 100644
--- a/src/widgets/widgets/qmenu_p.h
+++ b/src/widgets/widgets/qmenu_p.h
@@ -463,6 +463,7 @@ public:
void drawScroller(QPainter *painter, ScrollerTearOffItem::Type type, const QRect &rect);
void drawTearOff(QPainter *painter, const QRect &rect);
+ QRect rect() const;
};
#endif // QT_NO_MENU
diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
index b037cc2141..9109b2e8a3 100644
--- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
+++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
@@ -55,6 +55,20 @@ static inline void centerOnScreen(QWidget *w, const QSize &size)
w->move(QGuiApplication::primaryScreen()->availableGeometry().center() - offset);
}
+struct MenuMetrics {
+ int fw;
+ int hmargin;
+ int vmargin;
+ int tearOffHeight;
+
+ MenuMetrics(const QMenu *menu) {
+ fw = menu->style()->pixelMetric(QStyle::PM_MenuPanelWidth, nullptr, menu);
+ hmargin = menu->style()->pixelMetric(QStyle::PM_MenuHMargin, nullptr, menu);
+ vmargin = menu->style()->pixelMetric(QStyle::PM_MenuVMargin, nullptr, menu);
+ tearOffHeight = menu->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, nullptr, menu);
+ }
+};
+
static inline void centerOnScreen(QWidget *w)
{
centerOnScreen(w, w->geometry().size());
@@ -114,6 +128,8 @@ private slots:
void QTBUG_56917_wideMenuSize();
void QTBUG_56917_wideMenuScreenNumber();
void QTBUG_56917_wideSubmenuScreenNumber();
+ void menuSize_Scrolling_data();
+ void menuSize_Scrolling();
protected slots:
void onActivated(QAction*);
void onHighlighted(QAction*);
@@ -617,7 +633,10 @@ void tst_QMenu::tearOff()
QVERIFY(QTest::qWaitForWindowActive(menu.data()));
QVERIFY(!menu->isTearOffMenuVisible());
- QTest::mouseClick(menu.data(), Qt::LeftButton, 0, QPoint(3, 3), 10);
+ MenuMetrics mm(menu.data());
+ const int tearOffOffset = mm.fw + mm.vmargin + mm.tearOffHeight / 2;
+
+ QTest::mouseClick(menu.data(), Qt::LeftButton, 0, QPoint(10, tearOffOffset), 10);
QTRY_VERIFY(menu->isTearOffMenuVisible());
QPointer<QMenu> torn = getTornOffMenu();
QVERIFY(torn);
@@ -1373,5 +1392,169 @@ void tst_QMenu::QTBUG_56917_wideSubmenuScreenNumber()
}
}
+void tst_QMenu::menuSize_Scrolling_data()
+{
+ QTest::addColumn<int>("numItems");
+ QTest::addColumn<int>("topMargin");
+ QTest::addColumn<int>("bottomMargin");
+ QTest::addColumn<int>("leftMargin");
+ QTest::addColumn<int>("rightMargin");
+ QTest::addColumn<int>("topPadding");
+ QTest::addColumn<int>("bottomPadding");
+ QTest::addColumn<int>("leftPadding");
+ QTest::addColumn<int>("rightPadding");
+ QTest::addColumn<int>("border");
+ QTest::addColumn<bool>("scrollable");
+ QTest::addColumn<bool>("tearOff");
+
+ // test data
+ // a single column and non-scrollable menu with contents margins + border
+ QTest::newRow("data0") << 5 << 2 << 2 << 2 << 2 << 0 << 0 << 0 << 0 << 2 << false << false;
+ // a single column and non-scrollable menu with paddings + border
+ QTest::newRow("data1") << 5 << 0 << 0 << 0 << 0 << 2 << 2 << 2 << 2 << 2 << false << false;
+ // a single column and non-scrollable menu with contents margins + paddings + border
+ QTest::newRow("data2") << 5 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << false << false;
+ // a single column and non-scrollable menu with contents margins + paddings + border + tear-off
+ QTest::newRow("data3") << 5 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << false << true;
+ // a multi-column menu with contents margins + border
+ QTest::newRow("data4") << 80 << 2 << 2 << 2 << 2 << 0 << 0 << 0 << 0 << 2 << false << false;
+ // a multi-column menu with paddings + border
+ QTest::newRow("data5") << 80 << 0 << 0 << 0 << 0 << 2 << 2 << 2 << 2 << 2 << false << false;
+ // a multi-column menu with contents margins + paddings + border
+ QTest::newRow("data6") << 80 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << false << false;
+ // a multi-column menu with contents margins + paddings + border + tear-off
+ QTest::newRow("data7") << 80 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << false << true;
+ // a scrollable menu with contents margins + border
+ QTest::newRow("data8") << 80 << 2 << 2 << 2 << 2 << 0 << 0 << 0 << 0 << 2 << true << false;
+ // a scrollable menu with paddings + border
+ QTest::newRow("data9") << 80 << 0 << 0 << 0 << 0 << 2 << 2 << 2 << 2 << 2 << true << false;
+ // a scrollable menu with contents margins + paddings + border
+ QTest::newRow("data10") << 80 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << true << false;
+ // a scrollable menu with contents margins + paddings + border + tear-off
+ QTest::newRow("data11") << 80 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << true << true;
+}
+
+void tst_QMenu::menuSize_Scrolling()
+{
+ class TestMenu : public QMenu
+ {
+ public:
+ struct ContentsMargins
+ {
+ ContentsMargins(int l, int t, int r, int b)
+ : left(l), top(t), right(r), bottom(b) {}
+ int left;
+ int top;
+ int right;
+ int bottom;
+ };
+
+ struct MenuPaddings
+ {
+ MenuPaddings(int l, int t, int r, int b)
+ : left(l), top(t), right(r), bottom(b) {}
+ int left;
+ int top;
+ int right;
+ int bottom;
+ };
+
+ TestMenu(int numItems, const ContentsMargins &margins, const MenuPaddings &paddings,
+ int border, bool scrollable, bool tearOff)
+ : QMenu("Test Menu"),
+ m_numItems(numItems),
+ m_scrollable(scrollable),
+ m_tearOff(tearOff)
+ {
+ init(margins, paddings, border);
+ }
+
+ ~TestMenu() {}
+
+ private:
+ void showEvent(QShowEvent *e) Q_DECL_OVERRIDE
+ {
+ QVERIFY(actions().length() == m_numItems);
+
+ int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin, nullptr, this);
+ int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, nullptr, this);
+ int leftMargin, topMargin, rightMargin, bottomMargin;
+ getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
+ QRect lastItem = actionGeometry(actions().at(actions().length() - 1));
+ QSize s = size();
+ QCOMPARE( s.width(), lastItem.right() + fw + hmargin + rightMargin + 1);
+ QMenu::showEvent(e);
+ }
+
+ void init(const ContentsMargins &margins, const MenuPaddings &paddings, int border)
+ {
+ setLayoutDirection(Qt::LeftToRight);
+
+ setTearOffEnabled(m_tearOff);
+ setContentsMargins(margins.left, margins.top, margins.right, margins.bottom);
+ QString cssStyle("QMenu {menu-scrollable: ");
+ cssStyle += (m_scrollable ? QString::number(1) : QString::number(0));
+ cssStyle += "; border: ";
+ cssStyle += QString::number(border);
+ cssStyle += "px solid black; padding: ";
+ cssStyle += QString::number(paddings.top);
+ cssStyle += "px ";
+ cssStyle += QString::number(paddings.right);
+ cssStyle += "px ";
+ cssStyle += QString::number(paddings.bottom);
+ cssStyle += "px ";
+ cssStyle += QString::number(paddings.left);
+ cssStyle += "px;}";
+ setStyleSheet(cssStyle);
+ for (int i = 1; i <= m_numItems; i++)
+ addAction("MenuItem " + QString::number(i));
+ }
+
+ private:
+ int m_numItems;
+ bool m_scrollable;
+ bool m_tearOff;
+ };
+
+ QFETCH(int, numItems);
+ QFETCH(int, topMargin);
+ QFETCH(int, bottomMargin);
+ QFETCH(int, leftMargin);
+ QFETCH(int, rightMargin);
+ QFETCH(int, topPadding);
+ QFETCH(int, bottomPadding);
+ QFETCH(int, leftPadding);
+ QFETCH(int, rightPadding);
+ QFETCH(int, border);
+ QFETCH(bool, scrollable);
+ QFETCH(bool, tearOff);
+
+ qApp->setAttribute(Qt::AA_DontUseNativeMenuBar);
+
+ TestMenu::ContentsMargins margins(leftMargin, topMargin, rightMargin, bottomMargin);
+ TestMenu::MenuPaddings paddings(leftPadding, topPadding, rightPadding, bottomPadding);
+ TestMenu menu(numItems, margins, paddings, border, scrollable, tearOff);
+ menu.popup(QPoint(0,0));
+ centerOnScreen(&menu);
+ QVERIFY(QTest::qWaitForWindowExposed(&menu));
+
+ QList<QAction *> actions = menu.actions();
+ QCOMPARE(actions.length(), numItems);
+
+ MenuMetrics mm(&menu);
+ QTest::keyClick(&menu, Qt::Key_Home);
+ QTRY_COMPARE(menu.actionGeometry(actions.first()).y(), mm.fw + mm.vmargin + topMargin + (tearOff ? mm.tearOffHeight : 0));
+ QCOMPARE(menu.actionGeometry(actions.first()).x(), mm.fw + mm.hmargin + leftMargin);
+
+ if (!scrollable)
+ return;
+
+ QTest::keyClick(&menu, Qt::Key_End);
+ QTRY_COMPARE(menu.actionGeometry(actions.last()).right(),
+ menu.width() - mm.fw - mm.hmargin - leftMargin - 1);
+ QCOMPARE(menu.actionGeometry(actions.last()).bottom(),
+ menu.height() - mm.fw - mm.vmargin - bottomMargin - 1);
+}
+
QTEST_MAIN(tst_QMenu)
#include "tst_qmenu.moc"