167bool QScrollBarPrivate::updateHoverControl(
const QPoint &pos)
170 QRect lastHoverRect = hoverRect;
171 QStyle::SubControl lastHoverControl = hoverControl;
172 bool doesHover = q->testAttribute(Qt::WA_Hover);
173 if (lastHoverControl != newHoverControl(pos) && doesHover) {
174 q->update(lastHoverRect);
175 q->update(hoverRect);
181QStyle::SubControl QScrollBarPrivate::newHoverControl(
const QPoint &pos)
184 QStyleOptionSlider opt;
185 q->initStyleOption(&opt);
186 opt.subControls = QStyle::SC_All;
187 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, pos, q);
188 if (hoverControl == QStyle::SC_None)
191 hoverRect = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, hoverControl, q);
211void QScrollBarPrivate::flash()
214 QStyleOptionSlider opt;
215 q->initStyleOption(&opt);
216 if (!flashed && q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q)) {
223 if (!flashTimer.isActive())
224 flashTimer.start(0ns, q);
227void QScrollBarPrivate::activateControl(uint control,
int threshold)
229 QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction;
231 case QStyle::SC_ScrollBarAddPage:
232 action = QAbstractSlider::SliderPageStepAdd;
234 case QStyle::SC_ScrollBarSubPage:
235 action = QAbstractSlider::SliderPageStepSub;
237 case QStyle::SC_ScrollBarAddLine:
238 action = QAbstractSlider::SliderSingleStepAdd;
240 case QStyle::SC_ScrollBarSubLine:
241 action = QAbstractSlider::SliderSingleStepSub;
243 case QStyle::SC_ScrollBarFirst:
244 action = QAbstractSlider::SliderToMinimum;
246 case QStyle::SC_ScrollBarLast:
247 action = QAbstractSlider::SliderToMaximum;
254 q_func()->setRepeatAction(action, threshold);
255 q_func()->triggerAction(action);
259void QScrollBarPrivate::stopRepeatAction()
262 QStyle::SubControl tmp = pressedControl;
263 q->setRepeatAction(QAbstractSlider::SliderNoAction);
264 pressedControl = QStyle::SC_None;
266 if (tmp == QStyle::SC_ScrollBarSlider)
267 q->setSliderDown(
false);
269 QStyleOptionSlider opt;
270 q->initStyleOption(&opt);
271 q->repaint(q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, tmp, q));
281void QScrollBar::initStyleOption(QStyleOptionSlider *option)
const
286 Q_D(
const QScrollBar);
287 option->initFrom(
this);
288 option->subControls = QStyle::SC_None;
289 option->activeSubControls = QStyle::SC_None;
290 option->orientation = d->orientation;
291 option->minimum = d->minimum;
292 option->maximum = d->maximum;
293 option->sliderPosition = d->position;
294 option->sliderValue = d->value;
295 option->singleStep = d->singleStep;
296 option->pageStep = d->pageStep;
297 option->upsideDown = d->invertedAppearance;
298 if (d->orientation == Qt::Horizontal)
299 option->state |= QStyle::State_Horizontal;
300 if ((d->flashed || !d->transient) && style()->styleHint(QStyle::SH_ScrollBar_Transient, option,
this))
301 option->state |= QStyle::State_On;
351void QScrollBarPrivate::init()
354 invertedControls =
true;
355 pressedControl = hoverControl = QStyle::SC_None;
356 pointerOutsidePressedControl =
false;
359 transient = q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q);
361 q->setFocusPolicy(Qt::NoFocus);
362 QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Slider);
363 if (orientation == Qt::Vertical)
365 q->setSizePolicy(sp);
366 q->setAttribute(Qt::WA_WState_OwnSizePolicy,
false);
367 q->setAttribute(Qt::WA_OpaquePaintEvent);
381QMenu *QScrollBar::createStandardContextMenu(QPoint position)
384 const bool horiz = HORIZONTAL;
385 QMenu *menu =
new QMenu();
386 menu->setObjectName(
"qt_scrollbar_menu"_L1);
388 if (window() && window()->windowHandle()) {
389 if (
auto *menuTopData = QMenuPrivate::get(menu)->topData())
390 menuTopData->initialScreen = window()->windowHandle()->screen();
393 menu->addAction(tr(
"Scroll here"),
this, [
this, horiz, position] {
394 setValue(d_func()->pixelPosToRangeValue(horiz ? position.x() : position.y()));
396 menu->addSeparator();
397 menu->addAction(horiz ? tr(
"Left edge") : tr(
"Top"),
this, [
this] {
398 triggerAction(QAbstractSlider::SliderToMinimum);
400 menu->addAction(horiz ? tr(
"Right edge") : tr(
"Bottom"),
this, [
this] {
401 triggerAction(QAbstractSlider::SliderToMaximum);
403 menu->addSeparator();
404 menu->addAction(horiz ? tr(
"Page left") : tr(
"Page up"),
this, [
this] {
405 triggerAction(QAbstractSlider::SliderPageStepSub);
407 menu->addAction(horiz ? tr(
"Page right") : tr(
"Page down"),
this, [
this] {
408 triggerAction(QAbstractSlider::SliderPageStepAdd);
410 menu->addSeparator();
411 menu->addAction(horiz ? tr(
"Scroll left") : tr(
"Scroll up"),
this, [
this] {
412 triggerAction(QAbstractSlider::SliderSingleStepSub);
414 menu->addAction(horiz ? tr(
"Scroll right") : tr(
"Scroll down"),
this, [
this] {
415 triggerAction(QAbstractSlider::SliderSingleStepAdd);
441void QScrollBar::contextMenuEvent(QContextMenuEvent *event)
443 if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu,
nullptr,
this)) {
444 QAbstractSlider::contextMenuEvent(event);
449 QMenu *menu = createStandardContextMenu(event->pos());
453 menu->setAttribute(Qt::WA_DeleteOnClose);
454 menu->popup(event->globalPos());
462QSize QScrollBar::sizeHint()
const
465 QStyleOptionSlider opt;
466 initStyleOption(&opt);
468 int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt,
this);
469 int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt,
this);
471 if (opt.orientation == Qt::Horizontal)
472 size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
474 size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
476 return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size,
this);
488bool QScrollBar::event(QEvent *event)
491 switch(event->type()) {
492 case QEvent::HoverEnter:
493 case QEvent::HoverLeave:
494 case QEvent::HoverMove:
495 if (
const QHoverEvent *he =
static_cast<
const QHoverEvent *>(event))
496 d_func()->updateHoverControl(he->position().toPoint());
498 case QEvent::StyleChange: {
499 QStyleOptionSlider opt;
500 initStyleOption(&opt);
501 d_func()->setTransient(style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt,
this));
505 if (
static_cast<QTimerEvent *>(event)->id() == d->flashTimer.id()) {
506 QStyleOptionSlider opt;
507 initStyleOption(&opt);
508 if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt,
this)) {
512 d->flashTimer.stop();
518 return QAbstractSlider::event(event);
554void QScrollBar::paintEvent(QPaintEvent *)
557 QStylePainter p(
this);
558 QStyleOptionSlider opt;
559 initStyleOption(&opt);
560 opt.subControls = QStyle::SC_All;
561 if (d->pressedControl) {
562 opt.activeSubControls = (QStyle::SubControl)d->pressedControl;
563 if (!d->pointerOutsidePressedControl)
564 opt.state |= QStyle::State_Sunken;
566 opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
568 p.drawComplexControl(QStyle::CC_ScrollBar, opt);
574void QScrollBar::mousePressEvent(QMouseEvent *e)
578 if (d->repeatActionTimer.isActive())
579 d->stopRepeatAction();
581 bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition,
583 QStyleOptionSlider opt;
584 initStyleOption(&opt);
585 opt.keyboardModifiers = e->modifiers();
587 if (d->maximum == d->minimum
588 || (e->buttons() & (~e->button()))
589 || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MiddleButton))) {
594 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->position().toPoint(),
this);
595 d->pointerOutsidePressedControl =
false;
597 QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt,
598 QStyle::SC_ScrollBarSlider,
this);
599 QPoint click = e->position().toPoint();
600 QPoint pressValue = click - sr.center() + sr.topLeft();
601 d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) :
602 d->pixelPosToRangeValue(pressValue.y());
603 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
604 d->clickOffset =
HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y());
605 d->snapBackPosition = d->position;
608 if ((d->pressedControl == QStyle::SC_ScrollBarAddPage
609 || d->pressedControl == QStyle::SC_ScrollBarSubPage)
610 && ((midButtonAbsPos && e->button() == Qt::MiddleButton)
611 || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt,
this)
612 && e->button() == Qt::LeftButton))) {
613 int sliderLength =
HORIZONTAL ? sr.width() : sr.height();
614 setSliderPosition(d->pixelPosToRangeValue((
HORIZONTAL ? e->position().toPoint().x()
615 : e->position().toPoint().y()) - sliderLength / 2));
616 d->pressedControl = QStyle::SC_ScrollBarSlider;
617 d->clickOffset = sliderLength / 2;
619 const int initialDelay = 500;
622 d->activateControl(d->pressedControl, initialDelay);
623 repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl,
this));
624 if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) {
631 d->repeatActionTimer.start(50,
this);
633 if (d->pressedControl == QStyle::SC_ScrollBarSlider)
657void QScrollBar::mouseMoveEvent(QMouseEvent *e)
660 if (!d->pressedControl)
663 QStyleOptionSlider opt;
664 initStyleOption(&opt);
665 if (!(e->buttons() & Qt::LeftButton
666 || ((e->buttons() & Qt::MiddleButton)
667 && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt,
this))))
670 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
671 QPoint click = e->position().toPoint();
672 int newPosition = d->pixelPosToRangeValue((
HORIZONTAL ? click.x() : click.y()) -d->clickOffset);
673 int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt,
this);
676 r.adjust(-m, -m, m, m);
677 if (! r.contains(e->position().toPoint()))
678 newPosition = d->snapBackPosition;
680 setSliderPosition(newPosition);
681 }
else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt,
this)) {
683 if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt,
this)
684 && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
685 QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->position().toPoint(),
this);
686 if (newSc == d->pressedControl && !d->pointerOutsidePressedControl)
688 if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
689 d->pointerOutsidePressedControl =
false;
690 QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc,
this);
691 scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl,
this);
692 d->pressedControl = newSc;
693 d->activateControl(d->pressedControl, 0);
701 QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl,
this);
702 if (pr.contains(e->position().toPoint()) == d->pointerOutsidePressedControl) {
703 if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) {
704 d->pointerOutsidePressedControl =
true;
705 setRepeatAction(SliderNoAction);
708 d->activateControl(d->pressedControl);
715int QScrollBarPrivate::pixelPosToRangeValue(
int pos)
const
717 Q_Q(
const QScrollBar);
718 QStyleOptionSlider opt;
719 q->initStyleOption(&opt);
720 QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
721 QStyle::SC_ScrollBarGroove, q);
722 QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
723 QStyle::SC_ScrollBarSlider, q);
724 int sliderMin, sliderMax, sliderLength;
726 if (orientation == Qt::Horizontal) {
727 sliderLength = sr.width();
729 sliderMax = gr.right() - sliderLength + 1;
730 if (q->layoutDirection() == Qt::RightToLeft)
731 opt.upsideDown = !opt.upsideDown;
733 sliderLength = sr.height();
735 sliderMax = gr.bottom() - sliderLength + 1;
738 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
739 sliderMax - sliderMin, opt.upsideDown);