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:
Ali Kianian
2023-03-15 12:15:38 +02:00
parent 17feb6c352
commit 795aae8e7c
9 changed files with 340 additions and 66 deletions

View File

@@ -183,6 +183,7 @@ add_qtc_library(Utils
tooltip/tips.cpp tooltip/tips.h tooltip/tips.cpp tooltip/tips.h
tooltip/tooltip.cpp tooltip/tooltip.h tooltip/tooltip.cpp tooltip/tooltip.h
touchbar/touchbar.h touchbar/touchbar.h
transientscroll.cpp transientscroll.h
treemodel.cpp treemodel.h treemodel.cpp treemodel.h
treeviewcombobox.cpp treeviewcombobox.h treeviewcombobox.cpp treeviewcombobox.h
uncommentselection.cpp uncommentselection.h uncommentselection.cpp uncommentselection.h

View 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);
}

View 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;
};
}

View File

@@ -6,6 +6,7 @@
#include "formeditorwidget.h" #include "formeditorwidget.h"
#include "navigation2d.h" #include "navigation2d.h"
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/transientscroll.h>
#include <QAction> #include <QAction>
#include <QCoreApplication> #include <QCoreApplication>
@@ -36,6 +37,7 @@ FormEditorGraphicsView::FormEditorGraphicsView(QWidget *parent)
setBackgroundRole(QPalette::Window); setBackgroundRole(QPalette::Window);
activateCheckboardBackground(); activateCheckboardBackground();
Utils::TransientScrollAreaSupport::support(this);
// as mousetracking only works for mouse key it is better to handle it in the // 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 // eventFilter method so it works also for the space scrolling case as expected

View File

@@ -113,40 +113,6 @@ QScrollArea#dockWidgetScrollArea {
background: creatorTheme.DStabInactiveButtonPress; 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 */ /* Focus related styling */
ADS--DockWidgetTab[focused="true"] { ADS--DockWidgetTab[focused="true"] {
background: creatorTheme.DStabFocusBackground; background: creatorTheme.DStabFocusBackground;

View File

@@ -70,11 +70,6 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtr<TextEditor::Base
}); });
m_textEditor->editorWidget()->installEventFilter(this); 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);
} }
} }

View File

@@ -615,7 +615,6 @@ void StudioStyle::drawComplexControl(
painter->save(); painter->save();
painter->setRenderHint(QPainter::RenderHint::Antialiasing); painter->setRenderHint(QPainter::RenderHint::Antialiasing);
int lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget);
Theme::Color themeframeColor = enabled Theme::Color themeframeColor = enabled
? interaction ? interaction
? Theme::DSstateControlBackgroundColor_hover // Pressed ? Theme::DSstateControlBackgroundColor_hover // Pressed
@@ -627,14 +626,15 @@ void StudioStyle::drawComplexControl(
QColor frameColor = creatorTheme()->color(themeframeColor); QColor frameColor = creatorTheme()->color(themeframeColor);
if ((option->subControls & SC_SliderGroove) && groove.isValid()) { if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
Theme::Color bgPlusColor = enabled Theme::Color themeBgPlusColor = enabled
? interaction ? interaction
? Theme::DSstateControlBackgroundColor_hover // Pressed ? Theme::DSstateControlBackgroundColor_hover // Pressed
: grooveHover : grooveHover
? Theme::DSstateSeparatorColor // GrooveHover ? Theme::DSstateSeparatorColor // GrooveHover
: Theme::DStoolbarBackground // Idle : Theme::DSstateControlBackgroundColor_hover // Idle should be the same as pressed
: Theme::DStoolbarBackground; // Disabled : Theme::DStoolbarBackground; // Disabled
Theme::Color bgMinusColor = Theme::DSpopupBackground;
Theme::Color themeBgMinusColor = Theme::DSpopupBackground;
QRect minusRect(groove); QRect minusRect(groove);
QRect plusRect(groove); QRect plusRect(groove);
@@ -659,9 +659,9 @@ void StudioStyle::drawComplexControl(
painter->save(); painter->save();
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
painter->setBrush(creatorTheme()->color(bgPlusColor)); painter->setBrush(creatorTheme()->color(themeBgPlusColor));
painter->drawRoundedRect(plusRect, borderRadius, borderRadius); painter->drawRoundedRect(plusRect, borderRadius, borderRadius);
painter->setBrush(creatorTheme()->color(bgMinusColor)); painter->setBrush(creatorTheme()->color(themeBgMinusColor));
painter->drawRoundedRect(minusRect, borderRadius, borderRadius); painter->drawRoundedRect(minusRect, borderRadius, borderRadius);
painter->restore(); painter->restore();
} }
@@ -673,7 +673,8 @@ void StudioStyle::drawComplexControl(
: Theme::DSBackgroundColorAlternate : Theme::DSBackgroundColorAlternate
: Theme::DScontrolBackgroundDisabled; : Theme::DScontrolBackgroundDisabled;
painter->setPen(tickPen); painter->setBrush(Qt::NoBrush);
painter->setPen(creatorTheme()->color(tickPen));
int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget);
int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
int interval = slider->tickInterval; int interval = slider->tickInterval;
@@ -729,7 +730,7 @@ void StudioStyle::drawComplexControl(
} }
// draw handle // draw handle
if ((option->subControls & SC_SliderHandle) ) { if (option->subControls & SC_SliderHandle) {
Theme::Color handleColor = enabled Theme::Color handleColor = enabled
? interaction ? interaction
? Theme::DSinteraction // Interaction ? Theme::DSinteraction // Interaction
@@ -749,8 +750,9 @@ void StudioStyle::drawComplexControl(
} }
if (groove.isValid()) { if (groove.isValid()) {
int borderWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget);
painter->setBrush(Qt::NoBrush); painter->setBrush(Qt::NoBrush);
painter->setPen(QPen(frameColor, lineWidth)); painter->setPen(QPen(frameColor, borderWidth));
painter->drawRoundedRect(groove, borderRadius, borderRadius); painter->drawRoundedRect(groove, borderRadius, borderRadius);
} }
painter->restore(); painter->restore();
@@ -853,29 +855,48 @@ QRect StudioStyle::subControlRect(
} }
#endif #endif
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { switch (control)
switch (subControl) { {
case SubControl::SC_SliderGroove: case CC_Slider:
return option->rect; if (const auto slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
case SubControl::SC_SliderHandle: switch (subControl) {
{ case SubControl::SC_SliderGroove:
QRect retval = Super::subControlRect(control, option, subControl, widget); return slider->rect;
int thickness = 2; case SubControl::SC_SliderHandle:
QPoint center = retval.center(); {
const QRect &rect = slider->rect; QRect retval = Super::subControlRect(control, option, subControl, widget);
if (slider->orientation == Qt::Horizontal) return (slider->orientation == Qt::Horizontal)
return {center.x() - thickness, rect.top(), (thickness * 2) + 1, rect.height()}; ? retval.adjusted(0, 1, 0, 0)
else : retval.adjusted(1, 0, 0, 0);
return {rect.left(), center.y() - thickness, rect.width(), (thickness * 2) + 1}; }
} break;
break; default:
default: break;
break; }
} }
break;
default:
break;
} }
return Super::subControlRect(control, option, subControl, widget); 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( int StudioStyle::pixelMetric(
PixelMetric metric, PixelMetric metric,
const QStyleOption *option, const QStyleOption *option,
@@ -926,6 +947,21 @@ int StudioStyle::pixelMetric(
return 4; return 4;
case PM_ToolBarExtensionExtent: case PM_ToolBarExtensionExtent:
return 29; 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: default:
break; break;
} }

View File

@@ -51,6 +51,12 @@ public:
SubControl subControl, SubControl subControl,
const QWidget *widget) const override; const QWidget *widget) const override;
int styleHint(
StyleHint hint,
const QStyleOption *option,
const QWidget *widget,
QStyleHintReturn *returnData) const override;
int pixelMetric( int pixelMetric(
PixelMetric metric, PixelMetric metric,
const QStyleOption *option = nullptr, const QStyleOption *option = nullptr,

View File

@@ -66,6 +66,7 @@
#include <utils/textutils.h> #include <utils/textutils.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
#include <utils/tooltip/tooltip.h> #include <utils/tooltip/tooltip.h>
#include <utils/transientscroll.h>
#include <utils/uncommentselection.h> #include <utils/uncommentselection.h>
#include <QAbstractTextDocumentLayout> #include <QAbstractTextDocumentLayout>
@@ -1144,6 +1145,7 @@ TextEditorWidget::TextEditorWidget(QWidget *parent)
setLayoutDirection(Qt::LeftToRight); setLayoutDirection(Qt::LeftToRight);
viewport()->setMouseTracking(true); viewport()->setMouseTracking(true);
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
TransientScrollAreaSupport::support(this);
} }
void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc) void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc)