forked from qt-creator/qt-creator
QmlDesigner: Enable transient ScrollBars for StudioStyle
- Transient scroll bar is available for StudioStyle. - Some changes are applied to slider style, because scrollBar and sliders have some common usages within the style. Task-number: QDS-9283 Change-Id: I7a8b7997cf4d20142a0524c4a071b93dfd06321d Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
This commit is contained in:
@@ -183,6 +183,7 @@ add_qtc_library(Utils
|
||||
tooltip/tips.cpp tooltip/tips.h
|
||||
tooltip/tooltip.cpp tooltip/tooltip.h
|
||||
touchbar/touchbar.h
|
||||
transientscroll.cpp transientscroll.h
|
||||
treemodel.cpp treemodel.h
|
||||
treeviewcombobox.cpp treeviewcombobox.h
|
||||
uncommentselection.cpp uncommentselection.h
|
||||
|
||||
215
src/libs/utils/transientscroll.cpp
Normal file
215
src/libs/utils/transientscroll.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
// 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 void checkToFlashScroll(QPointer<ScrollBar> scrollBar, const QPoint &pos)
|
||||
{
|
||||
if (scrollBar.isNull())
|
||||
return;
|
||||
|
||||
if (!scrollBar->style()->styleHint(
|
||||
QStyle::SH_ScrollBar_Transient,
|
||||
nullptr, scrollBar))
|
||||
return;
|
||||
|
||||
if (scrollBarRect(scrollBar).contains(pos))
|
||||
scrollBar->flash();
|
||||
}
|
||||
|
||||
inline void checkToFlashScroll(const QPoint &pos)
|
||||
{
|
||||
checkToFlashScroll(verticalScrollBar, pos);
|
||||
checkToFlashScroll(horizontalScrollBar, pos);
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
51
src/libs/utils/transientscroll.h
Normal file
51
src/libs/utils/transientscroll.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QScrollBar>
|
||||
|
||||
class QAbstractScrollArea;
|
||||
|
||||
namespace Utils {
|
||||
class ScrollAreaPrivate;
|
||||
class ScrollBarPrivate;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT TransientScrollAreaSupport : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static void support(QAbstractScrollArea *scrollArea);
|
||||
virtual ~TransientScrollAreaSupport();
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
explicit TransientScrollAreaSupport(QAbstractScrollArea *scrollArea);
|
||||
|
||||
ScrollAreaPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT ScrollBar : public QScrollBar
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScrollBar(QWidget *parent = nullptr);
|
||||
virtual ~ScrollBar();
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
virtual void flash();
|
||||
|
||||
protected:
|
||||
virtual void initStyleOption(QStyleOptionSlider *option) const override;
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
private:
|
||||
ScrollBarPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "formeditorwidget.h"
|
||||
#include "navigation2d.h"
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/transientscroll.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QCoreApplication>
|
||||
@@ -36,6 +37,7 @@ FormEditorGraphicsView::FormEditorGraphicsView(QWidget *parent)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
|
||||
activateCheckboardBackground();
|
||||
Utils::TransientScrollAreaSupport::support(this);
|
||||
|
||||
// as mousetracking only works for mouse key it is better to handle it in the
|
||||
// eventFilter method so it works also for the space scrolling case as expected
|
||||
|
||||
@@ -113,40 +113,6 @@ QScrollArea#dockWidgetScrollArea {
|
||||
background: creatorTheme.DStabInactiveButtonPress;
|
||||
}
|
||||
|
||||
QScrollBar {
|
||||
background: creatorTheme.DSscrollBarTrack;
|
||||
}
|
||||
|
||||
QScrollBar:vertical {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
QScrollBar:horizontal {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
QScrollBar::handle {
|
||||
background: creatorTheme.DSscrollBarHandle;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:horizontal {
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
QScrollBar::add-line,
|
||||
QScrollBar::sub-line,
|
||||
QScrollBar::left-arrow,
|
||||
QScrollBar::right-arrow,
|
||||
QScrollBar::add-page,
|
||||
QScrollBar::sub-page {
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
/* Focus related styling */
|
||||
ADS--DockWidgetTab[focused="true"] {
|
||||
background: creatorTheme.DStabFocusBackground;
|
||||
|
||||
@@ -70,11 +70,6 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtr<TextEditor::Base
|
||||
});
|
||||
|
||||
m_textEditor->editorWidget()->installEventFilter(this);
|
||||
|
||||
static QString styleSheet = Theme::replaceCssColors(
|
||||
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")));
|
||||
m_textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet);
|
||||
m_textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -615,7 +615,6 @@ void StudioStyle::drawComplexControl(
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::RenderHint::Antialiasing);
|
||||
|
||||
int lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget);
|
||||
Theme::Color themeframeColor = enabled
|
||||
? interaction
|
||||
? Theme::DSstateControlBackgroundColor_hover // Pressed
|
||||
@@ -627,14 +626,15 @@ void StudioStyle::drawComplexControl(
|
||||
QColor frameColor = creatorTheme()->color(themeframeColor);
|
||||
|
||||
if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
|
||||
Theme::Color bgPlusColor = enabled
|
||||
Theme::Color themeBgPlusColor = enabled
|
||||
? interaction
|
||||
? Theme::DSstateControlBackgroundColor_hover // Pressed
|
||||
: grooveHover
|
||||
? Theme::DSstateSeparatorColor // GrooveHover
|
||||
: Theme::DStoolbarBackground // Idle
|
||||
: Theme::DSstateControlBackgroundColor_hover // Idle should be the same as pressed
|
||||
: Theme::DStoolbarBackground; // Disabled
|
||||
Theme::Color bgMinusColor = Theme::DSpopupBackground;
|
||||
|
||||
Theme::Color themeBgMinusColor = Theme::DSpopupBackground;
|
||||
|
||||
QRect minusRect(groove);
|
||||
QRect plusRect(groove);
|
||||
@@ -659,9 +659,9 @@ void StudioStyle::drawComplexControl(
|
||||
|
||||
painter->save();
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(creatorTheme()->color(bgPlusColor));
|
||||
painter->setBrush(creatorTheme()->color(themeBgPlusColor));
|
||||
painter->drawRoundedRect(plusRect, borderRadius, borderRadius);
|
||||
painter->setBrush(creatorTheme()->color(bgMinusColor));
|
||||
painter->setBrush(creatorTheme()->color(themeBgMinusColor));
|
||||
painter->drawRoundedRect(minusRect, borderRadius, borderRadius);
|
||||
painter->restore();
|
||||
}
|
||||
@@ -673,7 +673,8 @@ void StudioStyle::drawComplexControl(
|
||||
: Theme::DSBackgroundColorAlternate
|
||||
: Theme::DScontrolBackgroundDisabled;
|
||||
|
||||
painter->setPen(tickPen);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->setPen(creatorTheme()->color(tickPen));
|
||||
int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget);
|
||||
int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
|
||||
int interval = slider->tickInterval;
|
||||
@@ -729,7 +730,7 @@ void StudioStyle::drawComplexControl(
|
||||
}
|
||||
|
||||
// draw handle
|
||||
if ((option->subControls & SC_SliderHandle) ) {
|
||||
if (option->subControls & SC_SliderHandle) {
|
||||
Theme::Color handleColor = enabled
|
||||
? interaction
|
||||
? Theme::DSinteraction // Interaction
|
||||
@@ -749,8 +750,9 @@ void StudioStyle::drawComplexControl(
|
||||
}
|
||||
|
||||
if (groove.isValid()) {
|
||||
int borderWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->setPen(QPen(frameColor, lineWidth));
|
||||
painter->setPen(QPen(frameColor, borderWidth));
|
||||
painter->drawRoundedRect(groove, borderRadius, borderRadius);
|
||||
}
|
||||
painter->restore();
|
||||
@@ -853,29 +855,48 @@ QRect StudioStyle::subControlRect(
|
||||
}
|
||||
#endif
|
||||
|
||||
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
|
||||
switch (subControl) {
|
||||
case SubControl::SC_SliderGroove:
|
||||
return option->rect;
|
||||
case SubControl::SC_SliderHandle:
|
||||
{
|
||||
QRect retval = Super::subControlRect(control, option, subControl, widget);
|
||||
int thickness = 2;
|
||||
QPoint center = retval.center();
|
||||
const QRect &rect = slider->rect;
|
||||
if (slider->orientation == Qt::Horizontal)
|
||||
return {center.x() - thickness, rect.top(), (thickness * 2) + 1, rect.height()};
|
||||
else
|
||||
return {rect.left(), center.y() - thickness, rect.width(), (thickness * 2) + 1};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
switch (control)
|
||||
{
|
||||
case CC_Slider:
|
||||
if (const auto slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
|
||||
switch (subControl) {
|
||||
case SubControl::SC_SliderGroove:
|
||||
return slider->rect;
|
||||
case SubControl::SC_SliderHandle:
|
||||
{
|
||||
QRect retval = Super::subControlRect(control, option, subControl, widget);
|
||||
return (slider->orientation == Qt::Horizontal)
|
||||
? retval.adjusted(0, 1, 0, 0)
|
||||
: retval.adjusted(1, 0, 0, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Super::subControlRect(control, option, subControl, widget);
|
||||
}
|
||||
|
||||
int StudioStyle::styleHint(
|
||||
StyleHint hint,
|
||||
const QStyleOption *option,
|
||||
const QWidget *widget,
|
||||
QStyleHintReturn *returnData) const
|
||||
{
|
||||
switch (hint) {
|
||||
case SH_ScrollBar_Transient:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Super::styleHint(hint, option, widget, returnData);
|
||||
}
|
||||
|
||||
int StudioStyle::pixelMetric(
|
||||
PixelMetric metric,
|
||||
const QStyleOption *option,
|
||||
@@ -926,6 +947,21 @@ int StudioStyle::pixelMetric(
|
||||
return 4;
|
||||
case PM_ToolBarExtensionExtent:
|
||||
return 29;
|
||||
case PM_ScrollBarExtent:
|
||||
return 20;
|
||||
case PM_ScrollBarSliderMin:
|
||||
return 30;
|
||||
case PM_SliderLength:
|
||||
return 5;
|
||||
case PM_SliderThickness:
|
||||
if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
|
||||
return (slider->orientation == Qt::Horizontal
|
||||
? slider->rect.height()
|
||||
: slider->rect.width()) - 1;
|
||||
}
|
||||
break;
|
||||
case PM_SliderControlThickness:
|
||||
return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,12 @@ public:
|
||||
SubControl subControl,
|
||||
const QWidget *widget) const override;
|
||||
|
||||
int styleHint(
|
||||
StyleHint hint,
|
||||
const QStyleOption *option,
|
||||
const QWidget *widget,
|
||||
QStyleHintReturn *returnData) const override;
|
||||
|
||||
int pixelMetric(
|
||||
PixelMetric metric,
|
||||
const QStyleOption *option = nullptr,
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/theme/theme.h>
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
#include <utils/transientscroll.h>
|
||||
#include <utils/uncommentselection.h>
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
@@ -1144,6 +1145,7 @@ TextEditorWidget::TextEditorWidget(QWidget *parent)
|
||||
setLayoutDirection(Qt::LeftToRight);
|
||||
viewport()->setMouseTracking(true);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
TransientScrollAreaSupport::support(this);
|
||||
}
|
||||
|
||||
void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc)
|
||||
|
||||
Reference in New Issue
Block a user