From 795aae8e7c33a05a03b92a68b781b5918b63475b Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 15 Mar 2023 12:15:38 +0200 Subject: [PATCH] QmlDesigner: Enable transient ScrollBars for StudioStyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/transientscroll.cpp | 215 ++++++++++++++++++ src/libs/utils/transientscroll.h | 51 +++++ .../formeditor/formeditorgraphicsview.cpp | 2 + .../components/resources/dockwidgets.css | 34 --- .../texteditor/texteditorwidget.cpp | 5 - .../qmldesignerbase/utils/studiostyle.cpp | 90 +++++--- .../qmldesignerbase/utils/studiostyle.h | 6 + src/plugins/texteditor/texteditor.cpp | 2 + 9 files changed, 340 insertions(+), 66 deletions(-) create mode 100644 src/libs/utils/transientscroll.cpp create mode 100644 src/libs/utils/transientscroll.h diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 8777a6a881a..4362294341c 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -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 diff --git a/src/libs/utils/transientscroll.cpp b/src/libs/utils/transientscroll.cpp new file mode 100644 index 00000000000..e056eac7d0e --- /dev/null +++ b/src/libs/utils/transientscroll.cpp @@ -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 +#include +#include +#include +#include + +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, 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 viewPort = nullptr; + QPointer verticalScrollBar; + QPointer 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(); + 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(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(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); +} diff --git a/src/libs/utils/transientscroll.h b/src/libs/utils/transientscroll.h new file mode 100644 index 00000000000..2042bbf0fb0 --- /dev/null +++ b/src/libs/utils/transientscroll.h @@ -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 + +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; +}; + +} diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp index 1e67884ad8d..a9401480f3a 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -6,6 +6,7 @@ #include "formeditorwidget.h" #include "navigation2d.h" #include +#include #include #include @@ -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 diff --git a/src/plugins/qmldesigner/components/resources/dockwidgets.css b/src/plugins/qmldesigner/components/resources/dockwidgets.css index 3404065521e..4ff1b46f52d 100644 --- a/src/plugins/qmldesigner/components/resources/dockwidgets.css +++ b/src/plugins/qmldesigner/components/resources/dockwidgets.css @@ -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; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 9f4bb59231e..122228f2086 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -70,11 +70,6 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtreditorWidget()->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); } } diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp index ec0b0f4af52..4b2f1132274 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -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(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(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(option)) { + return (slider->orientation == Qt::Horizontal + ? slider->rect.height() + : slider->rect.width()) - 1; + } + break; + case PM_SliderControlThickness: + return 2; default: break; } diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.h b/src/plugins/qmldesignerbase/utils/studiostyle.h index a0eae302f54..4d6424cbef1 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.h +++ b/src/plugins/qmldesignerbase/utils/studiostyle.h @@ -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, diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 326be45d7fc..92c27723b99 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include @@ -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 &doc)