diff --git a/src/widgets/widgets/qscrollbar.cpp b/src/widgets/widgets/qscrollbar.cpp index 2cb517cb66a..431faf9e24d 100644 --- a/src/widgets/widgets/qscrollbar.cpp +++ b/src/widgets/widgets/qscrollbar.cpp @@ -9,17 +9,19 @@ #include "qstyle.h" #include "qstyleoption.h" #include "qstylepainter.h" -#if QT_CONFIG(menu) -#include "qmenu.h" -#endif #include #include - #if QT_CONFIG(accessibility) #include "qaccessible.h" #endif + +#if QT_CONFIG(menu) +#include "qmenu.h" +#include "private/qmenu_p.h" +#endif + #include #include "qscrollbar_p.h" @@ -27,6 +29,8 @@ using namespace std::chrono_literals; QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + /*! \class QScrollBar \brief The QScrollBar widget provides a vertical or horizontal scroll bar. @@ -364,50 +368,96 @@ void QScrollBarPrivate::init() } #ifndef QT_NO_CONTEXTMENU -/*! \reimp */ +/*! + \since 6.10 + + Creates the standard context menu, which is shown + when the user clicks on the scroll bar with the right mouse + button. It is called from the default contextMenuEvent() handler + and takes the \a position where the mouse click was in + this widget's local coordinates. + The popup menu's ownership is transferred to the caller. +*/ +QMenu *QScrollBar::createStandardContextMenu(QPoint position) +{ +#if QT_CONFIG(menu) + const bool horiz = HORIZONTAL; + QMenu *menu = new QMenu(); + menu->setObjectName("qt_scrollbar_menu"_L1); + + if (window() && window()->windowHandle()) { + if (auto *menuTopData = QMenuPrivate::get(menu)->topData()) + menuTopData->initialScreen = window()->windowHandle()->screen(); + } + + menu->addAction(tr("Scroll here"), this, [this, horiz, position] { + setValue(d_func()->pixelPosToRangeValue(horiz ? position.x() : position.y())); + }); + menu->addSeparator(); + menu->addAction(horiz ? tr("Left edge") : tr("Top"), this, [this] { + triggerAction(QAbstractSlider::SliderToMinimum); + }); + menu->addAction(horiz ? tr("Right edge") : tr("Bottom"), this, [this] { + triggerAction(QAbstractSlider::SliderToMaximum); + }); + menu->addSeparator(); + menu->addAction(horiz ? tr("Page left") : tr("Page up"), this, [this] { + triggerAction(QAbstractSlider::SliderPageStepSub); + }); + menu->addAction(horiz ? tr("Page right") : tr("Page down"), this, [this] { + triggerAction(QAbstractSlider::SliderPageStepAdd); + }); + menu->addSeparator(); + menu->addAction(horiz ? tr("Scroll left") : tr("Scroll up"), this, [this] { + triggerAction(QAbstractSlider::SliderSingleStepSub); + }); + menu->addAction(horiz ? tr("Scroll right") : tr("Scroll down"), this, [this] { + triggerAction(QAbstractSlider::SliderSingleStepAdd); + }); + return menu; +#else + Q_UNUSED(position); + return nullptr; +#endif // QT_CONFIG(menu) +} + +/*! + \reimp + \fn void QScrollBar::contextMenuEvent(QContextMenuEvent *event) + + Shows the standard context menu created with createStandardContextMenu(). + + If you do not want the scroll bar to have a context menu, you can set + its \l contextMenuPolicy to Qt::NoContextMenu. A style can also control + this behavior using the SH_ScrollBar_ContextMenu hint. + + If you want to customize the context menu, reimplement this function. + If you want to extend the standard context menu, reimplement this function, + call createStandardContextMenu() and extend the menu returned. Either store + the returned QMenu for later re-use or set the WA_DeleteOnClose attribute. + + Information about the event is passed in the \a event object. +*/ void QScrollBar::contextMenuEvent(QContextMenuEvent *event) { if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, nullptr, this)) { QAbstractSlider::contextMenuEvent(event); - return ; + return; } #if QT_CONFIG(menu) - bool horiz = HORIZONTAL; - QPointer menu = new QMenu(this); - QAction *actScrollHere = menu->addAction(tr("Scroll here")); - menu->addSeparator(); - QAction *actScrollTop = menu->addAction(horiz ? tr("Left edge") : tr("Top")); - QAction *actScrollBottom = menu->addAction(horiz ? tr("Right edge") : tr("Bottom")); - menu->addSeparator(); - QAction *actPageUp = menu->addAction(horiz ? tr("Page left") : tr("Page up")); - QAction *actPageDn = menu->addAction(horiz ? tr("Page right") : tr("Page down")); - menu->addSeparator(); - QAction *actScrollUp = menu->addAction(horiz ? tr("Scroll left") : tr("Scroll up")); - QAction *actScrollDn = menu->addAction(horiz ? tr("Scroll right") : tr("Scroll down")); - QAction *actionSelected = menu->exec(event->globalPos()); - delete menu; - if (actionSelected == nullptr) - /* do nothing */ ; - else if (actionSelected == actScrollHere) - setValue(d_func()->pixelPosToRangeValue(horiz ? event->pos().x() : event->pos().y())); - else if (actionSelected == actScrollTop) - triggerAction(QAbstractSlider::SliderToMinimum); - else if (actionSelected == actScrollBottom) - triggerAction(QAbstractSlider::SliderToMaximum); - else if (actionSelected == actPageUp) - triggerAction(QAbstractSlider::SliderPageStepSub); - else if (actionSelected == actPageDn) - triggerAction(QAbstractSlider::SliderPageStepAdd); - else if (actionSelected == actScrollUp) - triggerAction(QAbstractSlider::SliderSingleStepSub); - else if (actionSelected == actScrollDn) - triggerAction(QAbstractSlider::SliderSingleStepAdd); -#endif // QT_CONFIG(menu) + QMenu *menu = createStandardContextMenu(event->pos()); + if (!menu) + return; + + menu->setAttribute(Qt::WA_DeleteOnClose); + menu->popup(event->globalPos()); +#else + Q_UNUSED(pos) +#endif } #endif // QT_NO_CONTEXTMENU - /*! \reimp */ QSize QScrollBar::sizeHint() const { diff --git a/src/widgets/widgets/qscrollbar.h b/src/widgets/widgets/qscrollbar.h index 9fd8f617ab3..87f638983c7 100644 --- a/src/widgets/widgets/qscrollbar.h +++ b/src/widgets/widgets/qscrollbar.h @@ -27,6 +27,10 @@ public: QSize sizeHint() const override; bool event(QEvent *event) override; +#ifndef QT_NO_CONTEXTMENU + QMenu *createStandardContextMenu(QPoint position); +#endif + protected: #if QT_CONFIG(wheelevent) void wheelEvent(QWheelEvent *) override;