172bool QScrollBarPrivate::updateHoverControl(
const QPoint &pos)
175 QRect lastHoverRect = hoverRect;
176 QStyle::SubControl lastHoverControl = hoverControl;
177 bool doesHover = q->testAttribute(Qt::WA_Hover);
178 if (lastHoverControl != newHoverControl(pos) && doesHover) {
179 q->update(lastHoverRect);
180 q->update(hoverRect);
186QStyle::SubControl QScrollBarPrivate::newHoverControl(
const QPoint &pos)
189 QStyleOptionSlider opt;
190 q->initStyleOption(&opt);
191 opt.subControls = QStyle::SC_All;
192 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, pos, q);
193 if (hoverControl == QStyle::SC_None)
196 hoverRect = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, hoverControl, q);
216void QScrollBarPrivate::flash()
219 QStyleOptionSlider opt;
220 q->initStyleOption(&opt);
221 if (!flashed && q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q)) {
228 if (!flashTimer.isActive())
229 flashTimer.start(0ns, q);
232void QScrollBarPrivate::activateControl(uint control,
int threshold)
234 QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction;
236 case QStyle::SC_ScrollBarAddPage:
237 action = QAbstractSlider::SliderPageStepAdd;
239 case QStyle::SC_ScrollBarSubPage:
240 action = QAbstractSlider::SliderPageStepSub;
242 case QStyle::SC_ScrollBarAddLine:
243 action = QAbstractSlider::SliderSingleStepAdd;
245 case QStyle::SC_ScrollBarSubLine:
246 action = QAbstractSlider::SliderSingleStepSub;
248 case QStyle::SC_ScrollBarFirst:
249 action = QAbstractSlider::SliderToMinimum;
251 case QStyle::SC_ScrollBarLast:
252 action = QAbstractSlider::SliderToMaximum;
259 q_func()->setRepeatAction(action, threshold);
260 q_func()->triggerAction(action);
264void QScrollBarPrivate::stopRepeatAction()
267 QStyle::SubControl tmp = pressedControl;
268 q->setRepeatAction(QAbstractSlider::SliderNoAction);
269 pressedControl = QStyle::SC_None;
271 if (tmp == QStyle::SC_ScrollBarSlider)
272 q->setSliderDown(
false);
274 QStyleOptionSlider opt;
275 q->initStyleOption(&opt);
276 q->repaint(q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, tmp, q));
286void QScrollBar::initStyleOption(QStyleOptionSlider *option)
const
291 Q_D(
const QScrollBar);
292 option->initFrom(
this);
293 option->subControls = QStyle::SC_None;
294 option->activeSubControls = QStyle::SC_None;
295 option->orientation = d->orientation;
296 option->minimum = d->minimum;
297 option->maximum = d->maximum;
298 option->sliderPosition = d->position;
299 option->sliderValue = d->value;
300 option->singleStep = d->singleStep;
301 option->pageStep = d->pageStep;
302 option->upsideDown = d->invertedAppearance;
303 if (d->orientation == Qt::Horizontal)
304 option->state |= QStyle::State_Horizontal;
305 if ((d->flashed || !d->transient) && style()->styleHint(QStyle::SH_ScrollBar_Transient, option,
this))
306 option->state |= QStyle::State_On;
356void QScrollBarPrivate::init()
359 invertedControls =
true;
360 pressedControl = hoverControl = QStyle::SC_None;
361 pointerOutsidePressedControl =
false;
364 transient = q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q);
366 q->setFocusPolicy(Qt::NoFocus);
367 QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Slider);
368 if (orientation == Qt::Vertical)
370 q->setSizePolicy(sp);
371 q->setAttribute(Qt::WA_WState_OwnSizePolicy,
false);
372 q->setAttribute(Qt::WA_OpaquePaintEvent);
386QMenu *QScrollBar::createStandardContextMenu(QPoint position)
389 const bool horiz = HORIZONTAL;
390 QMenu *menu =
new QMenu(
this);
391 menu->setObjectName(
"qt_scrollbar_menu"_L1);
393 if (window() && window()->windowHandle()) {
394 if (
auto *menuTopData = QMenuPrivate::get(menu)->topData())
395 menuTopData->initialScreen = window()->windowHandle()->screen();
398 menu->addAction(tr(
"Scroll here"),
this, [
this, horiz, position] {
399 setValue(d_func()->pixelPosToRangeValue(horiz ? position.x() : position.y()));
401 menu->addSeparator();
402 menu->addAction(horiz ? tr(
"Left edge") : tr(
"Top"),
this, [
this] {
403 triggerAction(QAbstractSlider::SliderToMinimum);
405 menu->addAction(horiz ? tr(
"Right edge") : tr(
"Bottom"),
this, [
this] {
406 triggerAction(QAbstractSlider::SliderToMaximum);
408 menu->addSeparator();
409 menu->addAction(horiz ? tr(
"Page left") : tr(
"Page up"),
this, [
this] {
410 triggerAction(QAbstractSlider::SliderPageStepSub);
412 menu->addAction(horiz ? tr(
"Page right") : tr(
"Page down"),
this, [
this] {
413 triggerAction(QAbstractSlider::SliderPageStepAdd);
415 menu->addSeparator();
416 menu->addAction(horiz ? tr(
"Scroll left") : tr(
"Scroll up"),
this, [
this] {
417 triggerAction(QAbstractSlider::SliderSingleStepSub);
419 menu->addAction(horiz ? tr(
"Scroll right") : tr(
"Scroll down"),
this, [
this] {
420 triggerAction(QAbstractSlider::SliderSingleStepAdd);
446void QScrollBar::contextMenuEvent(QContextMenuEvent *event)
448 if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu,
nullptr,
this)) {
449 QAbstractSlider::contextMenuEvent(event);
454 QMenu *menu = createStandardContextMenu(event->pos());
458 menu->setAttribute(Qt::WA_DeleteOnClose);
459 menu->popup(event->globalPos());
467QSize QScrollBar::sizeHint()
const
470 QStyleOptionSlider opt;
471 initStyleOption(&opt);
473 int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt,
this);
474 int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt,
this);
476 if (opt.orientation == Qt::Horizontal)
477 size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
479 size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
481 return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size,
this);
493bool QScrollBar::event(QEvent *event)
496 switch(event->type()) {
497 case QEvent::HoverEnter:
498 case QEvent::HoverLeave:
499 case QEvent::HoverMove:
500 if (
const QHoverEvent *he =
static_cast<
const QHoverEvent *>(event))
501 d_func()->updateHoverControl(he->position().toPoint());
503 case QEvent::StyleChange: {
504 QStyleOptionSlider opt;
505 initStyleOption(&opt);
506 d_func()->setTransient(style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt,
this));
510 if (
static_cast<QTimerEvent *>(event)->id() == d->flashTimer.id()) {
511 QStyleOptionSlider opt;
512 initStyleOption(&opt);
513 if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt,
this)) {
517 d->flashTimer.stop();
523 return QAbstractSlider::event(event);
559void QScrollBar::paintEvent(QPaintEvent *)
562 QStylePainter p(
this);
563 QStyleOptionSlider opt;
564 initStyleOption(&opt);
565 opt.subControls = QStyle::SC_All;
566 if (d->pressedControl) {
567 opt.activeSubControls = (QStyle::SubControl)d->pressedControl;
568 if (!d->pointerOutsidePressedControl)
569 opt.state |= QStyle::State_Sunken;
571 opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
573 p.drawComplexControl(QStyle::CC_ScrollBar, opt);
579void QScrollBar::mousePressEvent(QMouseEvent *e)
583 if (d->repeatActionTimer.isActive())
584 d->stopRepeatAction();
586 bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition,
588 QStyleOptionSlider opt;
589 initStyleOption(&opt);
590 opt.keyboardModifiers = e->modifiers();
592 if (d->maximum == d->minimum
593 || (e->buttons() & (~e->button()))
594 || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MiddleButton))) {
599 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->position().toPoint(),
this);
600 d->pointerOutsidePressedControl =
false;
602 QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt,
603 QStyle::SC_ScrollBarSlider,
this);
604 QPoint click = e->position().toPoint();
605 QPoint pressValue = click - sr.center() + sr.topLeft();
606 d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) :
607 d->pixelPosToRangeValue(pressValue.y());
608 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
609 d->clickOffset =
HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y());
610 d->snapBackPosition = d->position;
613 if ((d->pressedControl == QStyle::SC_ScrollBarAddPage
614 || d->pressedControl == QStyle::SC_ScrollBarSubPage)
615 && ((midButtonAbsPos && e->button() == Qt::MiddleButton)
616 || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt,
this)
617 && e->button() == Qt::LeftButton))) {
618 int sliderLength =
HORIZONTAL ? sr.width() : sr.height();
619 setSliderPosition(d->pixelPosToRangeValue((
HORIZONTAL ? e->position().toPoint().x()
620 : e->position().toPoint().y()) - sliderLength / 2));
621 d->pressedControl = QStyle::SC_ScrollBarSlider;
622 d->clickOffset = sliderLength / 2;
624 const int initialDelay = 500;
627 d->activateControl(d->pressedControl, initialDelay);
628 repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl,
this));
629 if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) {
636 d->repeatActionTimer.start(50,
this);
638 if (d->pressedControl == QStyle::SC_ScrollBarSlider)
662void QScrollBar::mouseMoveEvent(QMouseEvent *e)
665 if (!d->pressedControl)
668 QStyleOptionSlider opt;
669 initStyleOption(&opt);
670 if (!(e->buttons() & Qt::LeftButton
671 || ((e->buttons() & Qt::MiddleButton)
672 && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt,
this))))
675 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
676 QPoint click = e->position().toPoint();
677 int newPosition = d->pixelPosToRangeValue((
HORIZONTAL ? click.x() : click.y()) -d->clickOffset);
678 int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt,
this);
681 r.adjust(-m, -m, m, m);
682 if (! r.contains(e->position().toPoint()))
683 newPosition = d->snapBackPosition;
685 setSliderPosition(newPosition);
686 }
else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt,
this)) {
688 if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt,
this)
689 && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
690 QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->position().toPoint(),
this);
691 if (newSc == d->pressedControl && !d->pointerOutsidePressedControl)
693 if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
694 d->pointerOutsidePressedControl =
false;
695 QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc,
this);
696 scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl,
this);
697 d->pressedControl = newSc;
698 d->activateControl(d->pressedControl, 0);
706 QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl,
this);
707 if (pr.contains(e->position().toPoint()) == d->pointerOutsidePressedControl) {
708 if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) {
709 d->pointerOutsidePressedControl =
true;
710 setRepeatAction(SliderNoAction);
713 d->activateControl(d->pressedControl);
720int QScrollBarPrivate::pixelPosToRangeValue(
int pos)
const
722 Q_Q(
const QScrollBar);
723 QStyleOptionSlider opt;
724 q->initStyleOption(&opt);
725 QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
726 QStyle::SC_ScrollBarGroove, q);
727 QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
728 QStyle::SC_ScrollBarSlider, q);
729 int sliderMin, sliderMax, sliderLength;
731 if (orientation == Qt::Horizontal) {
732 sliderLength = sr.width();
734 sliderMax = gr.right() - sliderLength + 1;
735 if (q->layoutDirection() == Qt::RightToLeft)
736 opt.upsideDown = !opt.upsideDown;
738 sliderLength = sr.height();
740 sliderMax = gr.bottom() - sliderLength + 1;
743 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
744 sliderMax - sliderMin, opt.upsideDown);