Files
qt-creator/src/libs/utils/transientscroll.cpp
Ali Kianian ca300e9eb8 QmlDesigner: Fix text selection bug for code editor
The event is returned to the parent hierarchy when the position is not
in the scrollbars area.

Task-number: QDS-9726
Change-Id: Id781650e15035a26f282990d7ee387b4417bc6eb
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
2023-04-19 14:11:23 +00:00

223 lines
6.1 KiB
C++

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "transientscroll.h"
#include <QAbstractScrollArea>
#include <QMouseEvent>
#include <QPointer>
#include <QStyle>
#include <QStyleOption>
using namespace Utils;
static constexpr char transientScrollAreaSupportName[] = "transientScrollAreSupport";
class Utils::ScrollAreaPrivate
{
public:
ScrollAreaPrivate(QAbstractScrollArea *area)
: area(area)
{
verticalScrollBar = new ScrollBar(area);
area->setVerticalScrollBar(verticalScrollBar);
horizontalScrollBar = new ScrollBar(area);
area->setHorizontalScrollBar(horizontalScrollBar);
area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
area->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
inline QRect scrollBarRect(ScrollBar *scrollBar)
{
QRect rect = viewPort ? viewPort->rect() : area->rect();
if (scrollBar->orientation() == Qt::Vertical) {
int mDiff = rect.width() - scrollBar->sizeHint().width();
return rect.adjusted(mDiff, 0, mDiff, 0);
} else {
int mDiff = rect.height() - scrollBar->sizeHint().height();
return rect.adjusted(0, mDiff, 0, mDiff);
}
}
inline bool checkToFlashScroll(QPointer<ScrollBar> scrollBar, const QPoint &pos)
{
if (scrollBar.isNull())
return false;
if (!scrollBar->style()->styleHint(
QStyle::SH_ScrollBar_Transient,
nullptr, scrollBar))
return false;
if (scrollBarRect(scrollBar).contains(pos)) {
scrollBar->flash();
return true;
}
return false;
}
inline bool checkToFlashScroll(const QPoint &pos)
{
bool coversScroll = checkToFlashScroll(verticalScrollBar, pos);
if (!coversScroll)
coversScroll |= checkToFlashScroll(horizontalScrollBar, pos);
return coversScroll;
}
inline void installViewPort(QObject *eventHandler) {
QWidget *viewPort = area->viewport();
if (viewPort
&& viewPort != this->viewPort
&& viewPort->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, viewPort)
&& (area->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff
|| area->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) {
viewPort->installEventFilter(eventHandler);
this->viewPort = viewPort;
}
}
inline void uninstallViewPort(QObject *eventHandler) {
if (viewPort) {
viewPort->removeEventFilter(eventHandler);
this->viewPort = nullptr;
}
}
QAbstractScrollArea *area = nullptr;
QPointer<QWidget> viewPort = nullptr;
QPointer<ScrollBar> verticalScrollBar;
QPointer<ScrollBar> horizontalScrollBar;
};
TransientScrollAreaSupport::TransientScrollAreaSupport(QAbstractScrollArea *scrollArea)
: QObject(scrollArea)
, d(new ScrollAreaPrivate(scrollArea))
{
scrollArea->installEventFilter(this);
}
void TransientScrollAreaSupport::support(QAbstractScrollArea *scrollArea)
{
QObject *prevSupport = scrollArea->property(transientScrollAreaSupportName)
.value<QObject *>();
if (!prevSupport)
scrollArea->setProperty(transientScrollAreaSupportName,
QVariant::fromValue(
new TransientScrollAreaSupport(scrollArea))
);
}
TransientScrollAreaSupport::~TransientScrollAreaSupport()
{
delete d;
}
bool TransientScrollAreaSupport::eventFilter(QObject *watched, QEvent *event)
{
switch (event->type()) {
case QEvent::Enter: {
if (watched == d->area)
d->installViewPort(this);
}
break;
case QEvent::Leave: {
if (watched == d->area)
d->uninstallViewPort(this);
}
break;
case QEvent::MouseMove: {
if (watched == d->viewPort){
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent) {
if (d->checkToFlashScroll(mouseEvent->pos()))
return true;
}
}
}
break;
default:
break;
}
return QObject::eventFilter(watched, event);
}
class Utils::ScrollBarPrivate {
public:
bool flashed = false;
int flashTimer = 0;
};
ScrollBar::ScrollBar(QWidget *parent)
: QScrollBar(parent)
, d(new ScrollBarPrivate)
{
}
ScrollBar::~ScrollBar()
{
delete d;
}
QSize ScrollBar::sizeHint() const
{
QSize sh = QScrollBar::sizeHint();
if (style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) {
constexpr int thickness = 10;
if (orientation() == Qt::Horizontal)
sh.setHeight(thickness);
else
sh.setWidth(thickness);
} else {
constexpr int thickness = 12;
if (orientation() == Qt::Horizontal)
sh.setHeight(thickness);
else
sh.setWidth(thickness);
}
return sh;
}
void ScrollBar::flash()
{
if (!d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) {
d->flashed = true;
if (!isVisible())
show();
else
update();
}
if (!d->flashTimer)
d->flashTimer = startTimer(0);
}
void ScrollBar::initStyleOption(QStyleOptionSlider *option) const
{
QScrollBar::initStyleOption(option);
if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this))
option->state |= QStyle::State_On;
}
bool ScrollBar::event(QEvent *event)
{
switch (event->type()) {
case QEvent::Timer:
if (static_cast<QTimerEvent *>(event)->timerId() == d->flashTimer) {
if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) {
d->flashed = false;
update();
}
killTimer(d->flashTimer);
d->flashTimer = 0;
}
break;
default:
break;
}
return QScrollBar::event(event);
}