QScrollBar: Add createStandardContextMenu

Modeled after QLineEdit::createStandardContextMenu, allows customizing
the scroll bar context menu without re-implementing the entire menu.

[ChangeLog][QtWidgets][QScrollBar] Added createStandardContextMenu()
method to allow extending the built-in context menu.

Change-Id: I4f9feaba81a44a8c366b7341e0eb6934e9403a41
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Kai Uwe Broulik 2025-01-29 20:16:35 +01:00
parent a28a9e194c
commit 75ecb176df
2 changed files with 92 additions and 38 deletions

View File

@ -9,17 +9,19 @@
#include "qstyle.h"
#include "qstyleoption.h"
#include "qstylepainter.h"
#if QT_CONFIG(menu)
#include "qmenu.h"
#endif
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qpointer.h>
#if QT_CONFIG(accessibility)
#include "qaccessible.h"
#endif
#if QT_CONFIG(menu)
#include "qmenu.h"
#include "private/qmenu_p.h"
#endif
#include <limits.h>
#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<QMenu> 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
{

View File

@ -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;