Krita Source Code Documentation
Loading...
Searching...
No Matches
KoToolBoxScrollArea_p.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2018 Alvin Wong <alvinhochun@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#ifndef KO_TOOLBOX_SCROLL_AREA_H
8#define KO_TOOLBOX_SCROLL_AREA_H
9
10#include "KoToolBox_p.h"
11#include "KoToolBoxLayout_p.h"
12
13#include <QScrollArea>
14#include <QScrollBar>
15#include <QScroller>
16#include <QStyleOption>
17#include <QToolButton>
18#include <KisKineticScroller.h>
19
20class KoToolBoxScrollArea : public QScrollArea
21{
22 Q_OBJECT
23public:
24 KoToolBoxScrollArea(KoToolBox *toolBox, QWidget *parent)
25 : QScrollArea(parent)
26 , m_toolBox(toolBox)
27 , m_orientation(Qt::Vertical)
28 , m_scrollPrev(new QToolButton(this))
29 , m_scrollNext(new QToolButton(this))
30 {
31 setFrameShape(QFrame::NoFrame);
32 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
33 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
34
36 setWidget(m_toolBox);
37
38 m_scrollPrev->setAutoRepeat(true);
39 m_scrollPrev->setAutoFillBackground(true);
40 m_scrollPrev->setFocusPolicy(Qt::NoFocus);
41 connect(m_scrollPrev, &QToolButton::clicked, this, &KoToolBoxScrollArea::doScrollPrev);
42 m_scrollNext->setAutoRepeat(true);
43 m_scrollNext->setAutoFillBackground(true);
44 m_scrollNext->setFocusPolicy(Qt::NoFocus);
45 connect(m_scrollNext, &QToolButton::clicked, this, &KoToolBoxScrollArea::doScrollNext);
46 // These are for filtering the mouse wheel events:
47 m_scrollPrev->installEventFilter(this);
48 m_scrollNext->installEventFilter(this);
49
50 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
51 if (!scroller) {
52 QScroller::grabGesture(viewport(), QScroller::MiddleMouseButtonGesture);
53 scroller = QScroller::scroller(viewport());
54 QScrollerProperties sp = scroller->scrollerProperties();
55
56 sp.setScrollMetric(QScrollerProperties::MaximumVelocity, 0.0);
57 sp.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.1);
58 sp.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.1);
59 sp.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0);
60 sp.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.4);
61
62 scroller->setScrollerProperties(sp);
63 }
64 connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
65 }
66
73
74 void setCompact(bool state) {
75 m_toolBox->setCompact(state);
77 }
78
79 Qt::Orientation orientation() const
80 {
81 return m_orientation;
82 }
83
84 QSize minimumSizeHint() const override
85 {
86 return m_toolBox->minimumSizeHint();
87 }
88
89 QSize sizeHint() const override
90 {
91 return m_toolBox->sizeHint();
92 }
93
94public Q_SLOTS:
95 void slotScrollerStateChange(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
96
97protected:
98 bool event(QEvent *event) override
99 {
100 if (event->type() == QEvent::LayoutRequest) {
101 // LayoutRequest can be triggered by icon changes, so resize the toolbox
102 layoutItems();
103 // The toolbox might have changed the sizeHint and minimumSizeHint
104 updateGeometry();
105 }
106 return QScrollArea::event(event);
107 }
108
109 bool eventFilter(QObject *watched, QEvent *event) override
110 {
111 // The toolbuttons eat the wheel events, so we filter them for our use.
112 if ((watched == m_scrollPrev || watched == m_scrollNext) && event->type() == QEvent::Wheel) {
113 wheelEvent(static_cast<QWheelEvent *>(event));
114 return true;
115 }
116 return QScrollArea::eventFilter(watched, event);
117 }
118
119 void resizeEvent(QResizeEvent *event) override
120 {
121 layoutItems();
122 QScrollArea::resizeEvent(event);
124 }
125
126 void wheelEvent(QWheelEvent *event) override
127 {
128 if (m_orientation == Qt::Vertical) {
129 QApplication::sendEvent(verticalScrollBar(), event);
130 } else {
131 QApplication::sendEvent(horizontalScrollBar(), event);
132 }
133 }
134
135 void scrollContentsBy(int dx, int dy) override
136 {
137 QScrollArea::scrollContentsBy(dx, dy);
139 }
140
141private Q_SLOTS:
143 {
144 if (m_orientation == Qt::Vertical) {
145 verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
146 } else {
147 horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
148 }
149 }
150
152 {
153 if (m_orientation == Qt::Vertical) {
154 verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
155 } else {
156 horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
157 }
158 }
159
160private:
162 {
163 QStyleOption opt;
164 opt.initFrom(this);
165 return style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, this);
166 }
167
169 {
171 QSize newSize = viewport()->size();
172 if (m_orientation == Qt::Vertical) {
173 newSize.setHeight(l->heightForWidth(newSize.width()));
174 } else {
175 newSize.setWidth(l->widthForHeight(newSize.height()));
176 }
177 m_toolBox->resize(newSize);
178
180 }
181
183 {
184 // We move the scroll buttons outside the widget rect instead of setting
185 // the visibility, because setting the visibility triggers a relayout
186 // of QAbstractScrollArea, which occasionally causes an offset bug when
187 // QScroller performs the overshoot animation. (Overshoot is done by
188 // moving the viewport widget, but the viewport position is reset during
189 // a relayout.)
190 const int scrollButtonWidth = this->scrollButtonWidth();
191 const QScrollBar *scrollbar = m_orientation == Qt::Vertical ? verticalScrollBar() : horizontalScrollBar();
192 const bool canPrev = scrollbar->value() != scrollbar->minimum();
193 const bool canNext = scrollbar->value() != scrollbar->maximum();
194 m_scrollPrev->setEnabled(canPrev);
195 m_scrollNext->setEnabled(canNext);
196 if (m_orientation == Qt::Vertical) {
197 m_scrollPrev->setArrowType(Qt::UpArrow);
198 m_scrollPrev->setGeometry(canPrev ? 0 : -width(), 0, width(), scrollButtonWidth);
199 m_scrollNext->setArrowType(Qt::DownArrow);
200 m_scrollNext->setGeometry(canNext? 0 : -width(), height() - scrollButtonWidth, width(), scrollButtonWidth);
201 } else if (isLeftToRight()) {
202 m_scrollPrev->setArrowType(Qt::LeftArrow);
203 m_scrollPrev->setGeometry(0, canPrev ? 0 : -height(), scrollButtonWidth, height());
204 m_scrollNext->setArrowType(Qt::RightArrow);
205 m_scrollNext->setGeometry(width() - scrollButtonWidth, canNext ? 0 : -height(), scrollButtonWidth, height());
206 } else {
207 // Right-to-left is mirrored.
208 m_scrollPrev->setArrowType(Qt::RightArrow);
209 m_scrollPrev->setGeometry(width() - scrollButtonWidth, canPrev ? 0 : -height(), scrollButtonWidth, height());
210 m_scrollNext->setArrowType(Qt::LeftArrow);
211 m_scrollNext->setGeometry(0, canNext ? 0 : -height(), scrollButtonWidth, height());
212 }
213 }
214
216 Qt::Orientation m_orientation;
217
218 QToolButton *m_scrollPrev;
219 QToolButton *m_scrollNext;
220};
221
222#endif // KO_TOOLBOX_SCROLL_AREA_H
int heightForWidth(int width) const override
int widthForHeight(int height) const
QSize sizeHint() const override
void setCompact(bool state)
void slotScrollerStateChange(QScroller::State state)
QSize minimumSizeHint() const override
Qt::Orientation orientation() const
KoToolBoxScrollArea(KoToolBox *toolBox, QWidget *parent)
bool eventFilter(QObject *watched, QEvent *event) override
void setOrientation(Qt::Orientation orientation)
void wheelEvent(QWheelEvent *event) override
bool event(QEvent *event) override
void scrollContentsBy(int dx, int dy) override
void resizeEvent(QResizeEvent *event) override
KoToolBoxLayout * toolBoxLayout() const
void setOrientation(Qt::Orientation orientation)
Set the orientation of the layout to orientation.
void setCompact(bool)
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)