diff options
Diffstat (limited to 'src/gui/widgets/qmenu.cpp')
-rw-r--r-- | src/gui/widgets/qmenu.cpp | 511 |
1 files changed, 243 insertions, 268 deletions
diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index fc55a2c..35b68b4 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -150,7 +150,6 @@ private: void QMenuPrivate::init() { Q_Q(QMenu); - activationRecursionGuard = false; #ifndef QT_NO_WHATSTHIS q->setAttribute(Qt::WA_CustomWhatsThis); #endif @@ -164,6 +163,12 @@ void QMenuPrivate::init() } } +int QMenuPrivate::scrollerHeight() const +{ + Q_Q(const QMenu); + return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q)); +} + //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't QRect QMenuPrivate::popupGeometry(int screen) const { @@ -194,57 +199,75 @@ QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const return ret; } -void QMenuPrivate::calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const +void QMenuPrivate::updateActionRects() const { Q_Q(const QMenu); - if (!itemsDirty) { - actionRects = this->actionRects; - actionList = this->actionList; + if (!itemsDirty) return; + + //let's reinitialize the buffer + actionRects.resize(actions.count()); + actionRects.fill(QRect()); + + //let's try to get the last visible action + int lastVisibleAction = actions.count() - 1; + for(;lastVisibleAction >= 0; --lastVisibleAction) { + const QAction *action = actions.at(lastVisibleAction); + if (action->isVisible()) { + //removing trailing separators + if (action->isSeparator() && collapsibleSeparators) + continue; + break; + } } - actionRects.clear(); - actionList.clear(); int max_column_width = 0, dh = popupGeometry(QApplication::desktop()->screenNumber(q)).height(), - ncols = 1, y = 0; - const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuHMargin, 0, q), - vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q), - icone = q->style()->pixelMetric(QStyle::PM_SmallIconSize, 0, q); + QStyle *style = q->style(); + const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, 0, q), + vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, 0, q), + icone = style->pixelMetric(QStyle::PM_SmallIconSize, 0, q); //for compatability now - will have to refactor this away.. tabWidth = 0; maxIconWidth = 0; hasCheckableItems = false; - QList<QAction*> items = filteredActions(); - for(int i = 0; i < items.count(); i++) { - QAction *action = items.at(i); - if (widgetItems.value(action)) + ncols = 1; + sloppyAction = 0; + + for (int i = 0; i < actions.count(); ++i) { + QAction *action = actions.at(i); + if (action->isSeparator() || !action->isVisible() || widgetItems.at(i)) continue; + //..and some members hasCheckableItems |= action->isCheckable(); QIcon is = action->icon(); if (!is.isNull()) { - uint miw = maxIconWidth; - maxIconWidth = qMax<uint>(miw, icone + 4); + maxIconWidth = qMax<uint>(maxIconWidth, icone + 4); } } //calculate size QFontMetrics qfm = q->fontMetrics(); - for(int i = 0; i < items.count(); i++) { - QAction *action = items.at(i); + bool previousWasSeparator = true; // this is true to allow removing the leading separators + for(int i = 0; i <= lastVisibleAction; i++) { + QAction *action = actions.at(i); - QFontMetrics fm(action->font().resolve(q->font())); - QSize sz; + if (!action->isVisible() || + (collapsibleSeparators && previousWasSeparator && action->isSeparator())) + continue; // we continue, this action will get an empty QRect + + previousWasSeparator = action->isSeparator(); //let the style modify the above size.. QStyleOptionMenuItem opt; q->initStyleOption(&opt, action); - opt.rect = q->rect(); + const QFontMetrics &fm = opt.fontMetrics; - if (QWidget *w = widgetItems.value(action)) { - sz=w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize()); + QSize sz; + if (QWidget *w = widgetItems.at(i)) { + sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize()); } else { //calc what I think the size is.. if (action->isSeparator()) { @@ -275,7 +298,7 @@ void QMenuPrivate::calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAc sz.setHeight(is_sz.height()); } } - sz = q->style()->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q); + sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q); } @@ -283,119 +306,59 @@ void QMenuPrivate::calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAc max_column_width = qMax(max_column_width, sz.width()); //wrapping if (!scroll && - y+sz.height()+vmargin > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { + y+sz.height()+vmargin > dh - (style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { ncols++; y = vmargin; } y += sz.height(); - //append item - actionRects.insert(action, QRect(0, 0, sz.width(), sz.height())); - actionList.append(action); + //update the item + actionRects[i] = QRect(0, 0, sz.width(), sz.height()); } } - if (tabWidth) - max_column_width += tabWidth; //finally add in the tab width + max_column_width += tabWidth; //finally add in the tab width //calculate position - int x = hmargin; - y = vmargin; - - for(int i = 0; i < actionList.count(); i++) { - QAction *action = actionList.at(i); - QRect &rect = actionRects[action]; + const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); + const int base_y = vmargin + fw + topmargin + + (scroll ? scroll->scrollOffset : 0) + + (tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q) : 0); + int x = hmargin + fw + leftmargin; + y = base_y; + + for(int i = 0; i < actions.count(); i++) { + QRect &rect = actionRects[i]; if (rect.isNull()) continue; if (!scroll && - y+rect.height() > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { - ncols--; - if (ncols < 0) - qWarning("QMenu: Column calculation mismatch (%d)", ncols); + y+rect.height() > dh - (style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { x += max_column_width + hmargin; - y = vmargin; + y = base_y; } rect.translate(x, y); //move rect.setWidth(max_column_width); //uniform width - y += rect.height(); - } -} -void QMenuPrivate::updateActions() -{ - Q_Q(const QMenu); - if (!itemsDirty) - return; - sloppyAction = 0; - calcActionRects(actionRects, actionList); - for (QHash<QAction *, QWidget *>::ConstIterator item = widgetItems.constBegin(), - end = widgetItems.constEnd(); item != end; ++item) { - QAction *action = item.key(); - QWidget *widget = item.value(); - widget->setGeometry(actionRect(action)); - widget->setVisible(action->isVisible()); - } - ncols = 1; - int last_left = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q); - if (!scroll) { - for(int i = 0; i < actionList.count(); i++) { - int left = actionRects.value(actionList.at(i)).left(); - if (left > last_left) { - last_left = left; - ncols++; - } + //we need to update the widgets geometry + if (QWidget *widget = widgetItems.at(i)) { + widget->setGeometry(rect); + widget->setVisible(actions.at(i)->isVisible()); } + + y += rect.height(); } itemsDirty = 0; } -QList<QAction *> QMenuPrivate::filteredActions() const +QRect QMenuPrivate::actionRect(QAction *act) const { - QList<QAction *> visibleActions; - int i = 0; - while (i < actions.count()) { - QAction *action = actions.at(i); - if (!action->isVisible()) { - ++i; - continue; - } - if (!action->isSeparator() || !collapsibleSeparators) { - visibleActions.append(action); - ++i; - continue; - } + int index = actions.indexOf(act); + if (index == -1) + return QRect(); - // no leading separators - if (!visibleActions.isEmpty()) - visibleActions.append(action); - - // skip double/tripple/etc. separators - while (i < actions.count() - && (!actions.at(i)->isVisible() || actions.at(i)->isSeparator())) - ++i; - } + updateActionRects(); - if (collapsibleSeparators) { - // remove trailing separators - while (!visibleActions.isEmpty() && visibleActions.last()->isSeparator()) - visibleActions.removeLast(); - } - - return visibleActions; -} - -QRect QMenuPrivate::actionRect(QAction *act) const -{ - Q_Q(const QMenu); - QRect ret = actionRects.value(act); - if (ret.isNull()) - return ret; - if (scroll) - ret.translate(0, scroll->scrollOffset); - if (tearoff) - ret.translate(0, q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q)); - const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); - ret.translate(fw+leftmargin, fw+topmargin); - return ret; + //we found the action + return actionRects.at(index); } static const qreal MenuFadeTimeInSec = 0.150; @@ -525,14 +488,17 @@ void QMenuPrivate::setSyncAction() void QMenuPrivate::setFirstActionActive() { Q_Q(QMenu); - const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q); - for(int i = 0, saccum = 0; i < actionList.count(); i++) { - QAction *act = actionList[i]; + updateActionRects(); + for(int i = 0, saccum = 0; i < actions.count(); i++) { + const QRect &rect = actionRects.at(i); + if (rect.isNull()) + continue; if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) { - saccum -= actionRects.value(act).height(); - if (saccum > scroll->scrollOffset-scrollerHeight) + saccum -= rect.height(); + if (saccum > scroll->scrollOffset - scrollerHeight()) continue; } + QAction *act = actions.at(i); if (!act->isSeparator() && (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q) || act->isEnabled())) { @@ -582,9 +548,10 @@ void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason popupAction(currentAction, popup, activateFirst); } q->update(actionRect(action)); - QWidget *widget = widgetItems.value(action); if (reason == SelectedFromKeyboard) { + const int actionIndex = actions.indexOf(action); + QWidget *widget = widgetItems.at(actionIndex); if (widget) { if (widget->focusPolicy() != Qt::NoFocus) widget->setFocus(Qt::TabFocusReason); @@ -628,10 +595,9 @@ QAction *QMenuPrivate::actionAt(QPoint p) const if (!q_func()->rect().contains(p)) //sanity check return 0; - for(int i = 0; i < actionList.count(); i++) { - QAction *act = actionList[i]; - if (actionRect(act).contains(p)) - return act; + for(int i = 0; i < actionRects.count(); i++) { + if (actionRects.at(i).contains(p)) + return actions.at(i); } return 0; } @@ -705,27 +671,25 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc Q_Q(QMenu); if (!scroll || !scroll->scrollFlags) return; + updateActionRects(); int newOffset = 0; - const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q); - const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollHeight : 0; - const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0; + const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0; + const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0; const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q); const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); if (location == QMenuScroller::ScrollTop) { - for(int i = 0, saccum = 0; i < actionList.count(); i++) { - QAction *act = actionList.at(i); - if (act == action) { + for(int i = 0, saccum = 0; i < actions.count(); i++) { + if (actions.at(i) == action) { newOffset = topScroll - saccum; break; } - saccum += actionRects.value(act).height(); + saccum += actionRects.at(i).height(); } } else { - for(int i = 0, saccum = 0; i < actionList.count(); i++) { - QAction *act = actionList.at(i); - saccum += actionRects.value(act).height(); - if (act == action) { + for(int i = 0, saccum = 0; i < actions.count(); i++) { + saccum += actionRects.at(i).height(); + if (actions.at(i) == action) { if (location == QMenuScroller::ScrollCenter) newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll); else @@ -734,7 +698,7 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc } } if(newOffset) - newOffset -= fw*2; + newOffset -= fw * 2; } //figure out which scroll flags @@ -742,8 +706,8 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc if (newOffset < 0) //easy and cheap one newScrollFlags |= QMenuScroller::ScrollUp; int saccum = newOffset; - for(int i = 0; i < actionList.count(); i++) { - saccum += actionRects.value(actionList.at(i)).height(); + for(int i = 0; i < actionRects.count(); i++) { + saccum += actionRects.at(i).height(); if (saccum > q->height()) { newScrollFlags |= QMenuScroller::ScrollDown; break; @@ -794,9 +758,19 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc } //actually update flags - scroll->scrollOffset = newOffset; - if (scroll->scrollOffset > 0) - scroll->scrollOffset = 0; + const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative + if (!itemsDirty && delta) { + //we've scrolled so we need to update the action rects + for (int i = 0; i < actionRects.count(); ++i) { + QRect ¤t = actionRects[i]; + current.moveTop(current.top() + delta); + + //we need to update the widgets geometry + if (QWidget *w = widgetItems.at(i)) + w->setGeometry(current); + } + } + scroll->scrollOffset += delta; scroll->scrollFlags = newScrollFlags; if (active) setCurrentAction(action); @@ -807,9 +781,12 @@ void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation loc void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active) { Q_Q(QMenu); + updateActionRects(); if(location == QMenuScroller::ScrollBottom) { - for(int i = actionList.size()-1; i >= 0; --i) { - QAction *act = actionList.at(i); + for(int i = actions.size()-1; i >= 0; --i) { + QAction *act = actions.at(i); + if (actionRects.at(i).isNull()) + continue; if (!act->isSeparator() && (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q) || act->isEnabled())) { @@ -821,8 +798,10 @@ void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool activ } } } else if(location == QMenuScroller::ScrollTop) { - for(int i = 0; i < actionList.size(); ++i) { - QAction *act = actionList.at(i); + for(int i = 0; i < actions.size(); ++i) { + QAction *act = actions.at(i); + if (actionRects.at(i).isNull()) + continue; if (!act->isSeparator() && (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q) || act->isEnabled())) { @@ -842,38 +821,33 @@ void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool pag Q_Q(QMenu); if (!scroll || !(scroll->scrollFlags & direction)) //not really possible... return; - const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q); - const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollHeight : 0; - const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0; + updateActionRects(); + const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0; + const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0; const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q); const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); const int offset = topScroll ? topScroll-vmargin : 0; if (direction == QMenuScroller::ScrollUp) { - for(int i = 0, saccum = 0; i < actionList.count(); i++) { - QAction *act = actionList.at(i); - const int iHeight = actionRects.value(act).height(); - saccum -= iHeight; + for(int i = 0, saccum = 0; i < actions.count(); i++) { + saccum -= actionRects.at(i).height(); if (saccum <= scroll->scrollOffset-offset) { - scrollMenu(act, page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active); + scrollMenu(actions.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active); break; } } } else if (direction == QMenuScroller::ScrollDown) { bool scrolled = false; - for(int i = 0, saccum = 0; i < actionList.count(); i++) { - QAction *act = actionList.at(i); - const int iHeight = actionRects.value(act).height(); + for(int i = 0, saccum = 0; i < actions.count(); i++) { + const int iHeight = actionRects.at(i).height(); saccum -= iHeight; if (saccum <= scroll->scrollOffset-offset) { const int scrollerArea = q->height() - botScroll - fw*2; int visible = (scroll->scrollOffset-offset) - saccum; - for(i++ ; i < actionList.count(); i++) { - act = actionList.at(i); - const int iHeight = actionRects.value(act).height(); - visible += iHeight; + for(i++ ; i < actions.count(); i++) { + visible += actionRects.at(i).height(); if (visible > scrollerArea - topScroll) { scrolled = true; - scrollMenu(act, page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active); + scrollMenu(actions.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active); break; } } @@ -896,13 +870,12 @@ 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()) { - const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q); 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()); else if (dir == QMenuScroller::ScrollDown) - isScroll = (pos.y() >= q->height()-scrollerHeight); + isScroll = (pos.y() >= q->height() - scrollerHeight()); if (isScroll) { scroll->scrollDirection = dir; break; @@ -911,19 +884,17 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) } } if (isScroll) { - if (!scroll->scrollTimer) - scroll->scrollTimer = new QBasicTimer; - scroll->scrollTimer->start(50, q); + scroll->scrollTimer.start(50, q); return true; - } else if (scroll->scrollTimer && scroll->scrollTimer->isActive()) { - scroll->scrollTimer->stop(); + } else { + scroll->scrollTimer.stop(); } } if (tearoff) { //let the tear off thingie "steal" the event.. QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q)); if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) - tearRect.translate(0, q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q)); + tearRect.translate(0, scrollerHeight()); q->update(tearRect); if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) { setCurrentAction(0); @@ -1178,7 +1149,8 @@ void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) else option->palette.setCurrentColorGroup(QPalette::Disabled); - option->font = action->font(); + option->font = action->font().resolve(font()); + option->fontMetrics = QFontMetrics(option->font); if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) { option->state |= QStyle::State_Selected @@ -1373,19 +1345,17 @@ QMenu::QMenu(QMenuPrivate &dd, QWidget *parent) QMenu::~QMenu() { Q_D(QMenu); - for (QHash<QAction *, QWidget *>::ConstIterator item = d->widgetItems.constBegin(), - end = d->widgetItems.constEnd(); item != end; ++item) { - QWidgetAction *action = static_cast<QWidgetAction *>(item.key()); - QWidget *widget = item.value(); - if (action && widget) + for (int i = 0; i < d->widgetItems.count(); ++i) { + if (QWidget *widget = d->widgetItems.at(i)) { + QWidgetAction *action = static_cast<QWidgetAction *>(d->actions.at(i)); action->releaseWidget(widget); + d->widgetItems[i] = 0; + } } - d->widgetItems.clear(); if (d->eventLoop) d->eventLoop->exit(); - if (d->tornPopup) - d->tornPopup->close(); + hideTearOffMenu(); } /*! @@ -1596,8 +1566,8 @@ void QMenu::setTearOffEnabled(bool b) Q_D(QMenu); if (d->tearoff == b) return; - if (!b && d->tornPopup) - d->tornPopup->close(); + if (!b) + hideTearOffMenu(); d->tearoff = b; d->itemsDirty = true; @@ -1632,8 +1602,8 @@ bool QMenu::isTearOffMenuVisible() const */ void QMenu::hideTearOffMenu() { - if (d_func()->tornPopup) - d_func()->tornPopup->close(); + if (QWidget *w = d_func()->tornPopup) + w->close(); } @@ -1732,35 +1702,34 @@ QSize QMenu::sizeHint() const { Q_D(const QMenu); ensurePolished(); - QMap<QAction*, QRect> actionRects; - QList<QAction*> actionList; - d->calcActionRects(actionRects, actionList); + d->updateActionRects(); QSize s; QStyleOption opt(0); opt.rect = rect(); opt.palette = palette(); opt.state = QStyle::State_None; - for (QMap<QAction*, QRect>::const_iterator i = actionRects.constBegin(); - i != actionRects.constEnd(); ++i) { - if (i.value().bottom() > s.height()) - s.setHeight(i.value().y()+i.value().height()); - if (i.value().right() > s.width()) - s.setWidth(i.value().right()); - } - if (d->tearoff) - s.rheight() += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, this); - if (const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this)) { - s.rwidth() += fw*2; - s.rheight() += fw*2; + for (int i = 0; i < d->actionRects.count(); ++i) { + const QRect &rect = d->actionRects.at(i); + if (rect.isNull()) + continue; + if (rect.bottom() >= s.height()) + s.setHeight(rect.y() + rect.height()); + 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. + if (const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this)) { + s.rwidth() += fw; + s.rheight() += fw; + } + s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this); s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this); - s += QSize(d->leftmargin + d->rightmargin, d->topmargin + d->bottommargin); + s += QSize(d->rightmargin, d->bottommargin); return style()->sizeFromContents(QStyle::CT_Menu, &opt, s.expandedTo(QApplication::globalStrut()), this); @@ -1802,7 +1771,7 @@ void QMenu::popup(const QPoint &p, QAction *atAction) ensurePolished(); // Get the right font emit aboutToShow(); - d->updateActions(); + d->updateActionRects(); QPoint pos = p; QSize size = sizeHint(); QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(p)); @@ -1811,10 +1780,10 @@ void QMenu::popup(const QPoint &p, QAction *atAction) if (d->ncols > 1) { pos.setY(screen.top()+desktopFrame); } else if (atAction) { - for(int i=0, above_height=0; i<(int)d->actionList.count(); i++) { - QAction *action = d->actionList.at(i); + for(int i = 0, above_height = 0; i < d->actions.count(); i++) { + QAction *action = d->actions.at(i); if (action == atAction) { - int newY = pos.y()-above_height; + int newY = pos.y() - above_height; if (d->scroll && newY < desktopFrame) { d->scroll->scrollFlags = d->scroll->scrollFlags | QMenuPrivate::QMenuScroller::ScrollUp; @@ -1826,13 +1795,13 @@ void QMenu::popup(const QPoint &p, QAction *atAction) if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) { int below_height = above_height + d->scroll->scrollOffset; - for(int i2 = i; i2 < d->actionList.count(); i2++) - below_height += d->actionRects.value(d->actionList.at(i2)).height(); + for(int i2 = i; i2 < d->actionRects.count(); i2++) + below_height += d->actionRects.at(i2).height(); size.setHeight(below_height); } break; } else { - above_height += d->actionRects.value(action).height(); + above_height += d->actionRects.at(i).height(); } } } @@ -2039,8 +2008,7 @@ QAction *QMenu::exec(const QPoint &p, QAction *action) QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent) { QMenu menu(parent); - for(QList<QAction*>::ConstIterator it = actions.constBegin(); it != actions.constEnd(); ++it) - menu.addAction((*it)); + menu.addActions(actions); return menu.exec(pos, at); } @@ -2090,6 +2058,8 @@ void QMenu::hideEvent(QHideEvent *) d->hasHadMouse = false; d->causedPopup.widget = 0; d->causedPopup.action = 0; + if (d->scroll) + d->scroll->scrollTimer.stop(); //make sure the timer stops } /*! @@ -2098,6 +2068,7 @@ void QMenu::hideEvent(QHideEvent *) void QMenu::paintEvent(QPaintEvent *e) { Q_D(QMenu); + d->updateActionRects(); QPainter p(this); QRegion emptyArea = QRegion(rect()); @@ -2110,11 +2081,11 @@ void QMenu::paintEvent(QPaintEvent *e) style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this); //draw the items that need updating.. - for (int i = 0; i < d->actionList.count(); ++i) { - QAction *action = d->actionList.at(i); - QRect adjustedActionRect = d->actionRect(action); + 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) - || d->widgetItems.value(action)) + || d->widgetItems.at(i)) continue; //set the clip region to be extra safe (and adjust for the scrollers) QRegion adjustedActionReg(adjustedActionRect); @@ -2130,18 +2101,17 @@ void QMenu::paintEvent(QPaintEvent *e) const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this); //draw the scroller regions.. if (d->scroll) { - const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this); menuOpt.menuItemType = QStyleOptionMenuItem::Scroller; menuOpt.state |= QStyle::State_Enabled; if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) { - menuOpt.rect.setRect(fw, fw, width() - (fw * 2), scrollerHeight); + 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() - scrollerHeight - fw, width() - (fw * 2), - scrollerHeight); + 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); @@ -2154,7 +2124,7 @@ void QMenu::paintEvent(QPaintEvent *e) 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, style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this)); + menuOpt.rect.translate(0, d->scrollerHeight()); emptyArea -= QRegion(menuOpt.rect); p.setClipRect(menuOpt.rect); menuOpt.state = QStyle::State_None; @@ -2263,7 +2233,7 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) break; } } - if (e->button() || isContextMenu) + if (e->button() == Qt::LeftButton || isContextMenu) #endif d->activateAction(action, QAction::Trigger); } @@ -2344,11 +2314,11 @@ QMenu::event(QEvent *e) setMask(menuMask.region); } d->itemsDirty = 1; - d->updateActions(); + d->updateActionRects(); break; } case QEvent::Show: d->mouseDown = 0; - d->updateActions(); + d->updateActionRects(); if (d->currentAction) d->popupAction(d->currentAction, 0, false); break; @@ -2384,6 +2354,7 @@ bool QMenu::focusNextPrevChild(bool next) void QMenu::keyPressEvent(QKeyEvent *e) { Q_D(QMenu); + d->updateActionRects(); int key = e->key(); if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed if (key == Qt::Key_Left) @@ -2435,8 +2406,10 @@ void QMenu::keyPressEvent(QKeyEvent *e) QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay; if (!d->currentAction) { if(key == Qt::Key_Down) { - for(int i = 0; i < d->actionList.size(); ++i) { - QAction *act = d->actionList.at(i); + for(int i = 0; i < d->actions.count(); ++i) { + QAction *act = d->actions.at(i); + if (d->actionRects.at(i).isNull()) + continue; if (!act->isSeparator() && (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this) || act->isEnabled())) { @@ -2445,8 +2418,10 @@ void QMenu::keyPressEvent(QKeyEvent *e) } } } else { - for(int i = d->actionList.size()-1; i >= 0; --i) { - QAction *act = d->actionList.at(i); + for(int i = d->actions.count()-1; i >= 0; --i) { + QAction *act = d->actions.at(i); + if (d->actionRects.at(i).isNull()) + continue; if (!act->isSeparator() && (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this) || act->isEnabled())) { @@ -2456,8 +2431,8 @@ void QMenu::keyPressEvent(QKeyEvent *e) } } } else { - for(int i=0, y=0; !nextAction && i < (int)d->actionList.count(); i++) { - QAction *act = d->actionList.at(i); + for(int i = 0, y = 0; !nextAction && i < d->actions.count(); i++) { + QAction *act = d->actions.at(i); if (act == d->currentAction) { if (key == Qt::Key_Up) { for(int next_i = i-1; true; next_i--) { @@ -2466,21 +2441,23 @@ void QMenu::keyPressEvent(QKeyEvent *e) break; if (d->scroll) scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom; - next_i = d->actionList.count()-1; + next_i = d->actionRects.count()-1; } - QAction *next = d->actionList.at(next_i); + QAction *next = d->actions.at(next_i); if (next == d->currentAction) break; + if (d->actionRects.at(next_i).isNull()) + continue; if (next->isSeparator() || (!next->isEnabled() && !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this))) continue; nextAction = next; if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) { - int topVisible = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this); + int topVisible = d->scrollerHeight(); if (d->tearoff) topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this); - if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.value(nextAction).height()) + if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height()) scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop; } break; @@ -2488,31 +2465,32 @@ void QMenu::keyPressEvent(QKeyEvent *e) if (!nextAction && d->tearoff) d->tearoffHighlighted = 1; } else { - y += d->actionRects.value(act).height(); + y += d->actionRects.at(i).height(); for(int next_i = i+1; true; next_i++) { - if (next_i == d->actionList.count()) { + if (next_i == d->actionRects.count()) { if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this)) break; if (d->scroll) scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop; next_i = 0; } - QAction *next = d->actionList.at(next_i); + QAction *next = d->actions.at(next_i); if (next == d->currentAction) break; + if (d->actionRects.at(next_i).isNull()) + continue; if (next->isSeparator() || (!next->isEnabled() && !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this))) continue; nextAction = next; if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) { - const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this); - int bottomVisible = height()-scrollerHeight; + int bottomVisible = height() - d->scrollerHeight(); if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) - bottomVisible -= scrollerHeight; + bottomVisible -= d->scrollerHeight(); if (d->tearoff) bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this); - if ((y + d->scroll->scrollOffset + d->actionRects.value(nextAction).height()) > bottomVisible) + if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible) scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom; } break; @@ -2520,13 +2498,12 @@ void QMenu::keyPressEvent(QKeyEvent *e) } break; } - y += d->actionRects.value(act).height(); + y += d->actionRects.at(i).height(); } } if (nextAction) { if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) { - if (d->scroll->scrollTimer) - d->scroll->scrollTimer->stop(); + d->scroll->scrollTimer.stop(); d->scrollMenu(nextAction, scroll_loc); } d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard); @@ -2648,9 +2625,11 @@ void QMenu::keyPressEvent(QKeyEvent *e) int best_match_count = 0; d->searchBufferTimer.start(2000, this); d->searchBuffer += e->text(); - for(int i = 0; i < d->actionList.size(); ++i) { + for(int i = 0; i < d->actions.size(); ++i) { int match_count = 0; - register QAction *act = d->actionList.at(i); + if (d->actionRects.at(i).isNull()) + continue; + QAction *act = d->actions.at(i); const QString act_text = act->text(); for(int c = 0; c < d->searchBuffer.size(); ++c) { if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1) @@ -2667,8 +2646,10 @@ void QMenu::keyPressEvent(QKeyEvent *e) int clashCount = 0; QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0; QChar c = e->text().at(0).toUpper(); - for(int i = 0; i < d->actionList.size(); ++i) { - register QAction *act = d->actionList.at(i); + for(int i = 0; i < d->actions.size(); ++i) { + if (d->actionRects.at(i).isNull()) + continue; + QAction *act = d->actions.at(i); QKeySequence sequence = QKeySequence::mnemonic(act->text()); int key = sequence[0] & 0xffff; if (key == c.unicode()) { @@ -2739,7 +2720,7 @@ void QMenu::mouseMoveEvent(QMouseEvent *e) d->motions++; if (d->motions == 0) // ignore first mouse move event (see enterEvent()) return; - d->hasHadMouse |= rect().contains(e->pos()); + d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos()); QAction *action = d->actionAt(e->pos()); if (!action) { @@ -2783,10 +2764,10 @@ void QMenu::timerEvent(QTimerEvent *e) { Q_D(QMenu); - if (d->scroll && d->scroll->scrollTimer && d->scroll->scrollTimer->timerId() == e->timerId()) { + if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) { d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection); if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone) - d->scroll->scrollTimer->stop(); + d->scroll->scrollTimer.stop(); } else if(QMenuPrivate::menuDelayTimer.timerId() == e->timerId()) { QMenuPrivate::menuDelayTimer.stop(); internalDelayedPopup(); @@ -2813,31 +2794,25 @@ void QMenu::actionEvent(QActionEvent *e) connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered())); connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered())); } + QWidget *widget = 0; + if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) + widget = wa->requestWidget(this); + + int index = d->actions.indexOf(e->action()); + Q_ASSERT(index != -1); + d->widgetItems.insert(index, widget); - if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) { - QWidget *widget = wa->requestWidget(this); - if (widget) - d->widgetItems.insert(wa, widget); - } } else if (e->type() == QEvent::ActionRemoved) { - d->actionRects.clear(); - d->actionList.clear(); e->action()->disconnect(this); if (e->action() == d->currentAction) d->currentAction = 0; + int index = d->actions.indexOf(e->before()) + 1; if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) { - QWidget *widget = d->widgetItems.take(wa); - if (widget) + if (QWidget *widget = d->widgetItems.at(index)) wa->releaseWidget(widget); - } else { - // If this is called from the QAction destructor, the - // previous call to qobject_cast will fail because the - // QWidgetAction has been destroyed already. We need to - // remove it from the hash anyway or it might crash later - // the widget itself has been already destroyed in - // ~QWidgetAction - d->widgetItems.remove(e->action()); } + Q_ASSERT(index != -1); + d->widgetItems.removeAt(index); } #ifdef Q_WS_MAC @@ -2863,7 +2838,7 @@ void QMenu::actionEvent(QActionEvent *e) #endif if (isVisible()) { - d->updateActions(); + d->updateActionRects(); resize(sizeHint()); update(); } @@ -3044,7 +3019,7 @@ void QMenu::setSeparatorsCollapsible(bool collapse) d->collapsibleSeparators = collapse; d->itemsDirty = 1; if (isVisible()) { - d->updateActions(); + d->updateActionRects(); update(); } } @@ -3103,9 +3078,9 @@ int QMenu::insertSeparator(int index) QAction *QMenu::findActionForId(int id) const { - QList<QAction *> list = actions(); - for (int i = 0; i < list.size(); ++i) { - QAction *act = list.at(i); + Q_D(const QMenu); + for (int i = 0; i < d->actions.size(); ++i) { + QAction *act = d->actions.at(i); if (findIdForAction(act)== id) return act; } |