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
67 void setOrientation(Qt::Orientation orientation)
68 {
70 return;
71 }
75 }
76
77 Qt::Orientation orientation() const
78 {
79 return m_orientation;
80 }
81
82 QSize minimumSizeHint() const override
83 {
84 return m_toolBox->minimumSizeHint();
85 }
86
87 QSize sizeHint() const override
88 {
89 return m_toolBox->sizeHint();
90 }
91
92public Q_SLOTS:
93 void slotScrollerStateChange(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
94
95protected:
96 bool event(QEvent *event) override
97 {
98 if (event->type() == QEvent::LayoutRequest) {
99 // LayoutRequest can be triggered by icon changes, so resize the toolbox
100 layoutItems();
101 // The toolbox might have changed the sizeHint and minimumSizeHint
102 updateGeometry();
103 }
104 return QScrollArea::event(event);
105 }
106
107 bool eventFilter(QObject *watched, QEvent *event) override
108 {
109 // The toolbuttons eat the wheel events, so we filter them for our use.
110 if ((watched == m_scrollPrev || watched == m_scrollNext) && event->type() == QEvent::Wheel) {
111 wheelEvent(static_cast<QWheelEvent *>(event));
112 return true;
113 }
114 return QScrollArea::eventFilter(watched, event);
115 }
116
117 void resizeEvent(QResizeEvent *event) override
118 {
119 layoutItems();
120 QScrollArea::resizeEvent(event);
122 }
123
124 void wheelEvent(QWheelEvent *event) override
125 {
126 if (m_orientation == Qt::Vertical) {
127 QApplication::sendEvent(verticalScrollBar(), event);
128 } else {
129 QApplication::sendEvent(horizontalScrollBar(), event);
130 }
131 }
132
133 void scrollContentsBy(int dx, int dy) override
134 {
135 QScrollArea::scrollContentsBy(dx, dy);
137 }
138
139private Q_SLOTS:
141 {
142 if (m_orientation == Qt::Vertical) {
143 verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
144 } else {
145 horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
146 }
147 }
148
150 {
151 if (m_orientation == Qt::Vertical) {
152 verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
153 } else {
154 horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
155 }
156 }
157
158private:
160 {
161 QStyleOption opt;
162 opt.initFrom(this);
163 return style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, this);
164 }
165
167 {
169 QSize newSize = viewport()->size();
170 if (m_orientation == Qt::Vertical) {
171 newSize.setHeight(l->heightForWidth(newSize.width()));
172 } else {
173 newSize.setWidth(l->widthForHeight(newSize.height()));
174 }
175 m_toolBox->resize(newSize);
176
178 }
179
181 {
182 // We move the scroll buttons outside the widget rect instead of setting
183 // the visibility, because setting the visibility triggers a relayout
184 // of QAbstractScrollArea, which occasionally causes an offset bug when
185 // QScroller performs the overshoot animation. (Overshoot is done by
186 // moving the viewport widget, but the viewport position is reset during
187 // a relayout.)
188 const int scrollButtonWidth = this->scrollButtonWidth();
189 const QScrollBar *scrollbar = m_orientation == Qt::Vertical ? verticalScrollBar() : horizontalScrollBar();
190 const bool canPrev = scrollbar->value() != scrollbar->minimum();
191 const bool canNext = scrollbar->value() != scrollbar->maximum();
192 m_scrollPrev->setEnabled(canPrev);
193 m_scrollNext->setEnabled(canNext);
194 if (m_orientation == Qt::Vertical) {
195 m_scrollPrev->setArrowType(Qt::UpArrow);
196 m_scrollPrev->setGeometry(canPrev ? 0 : -width(), 0, width(), scrollButtonWidth);
197 m_scrollNext->setArrowType(Qt::DownArrow);
198 m_scrollNext->setGeometry(canNext? 0 : -width(), height() - scrollButtonWidth, width(), scrollButtonWidth);
199 } else if (isLeftToRight()) {
200 m_scrollPrev->setArrowType(Qt::LeftArrow);
201 m_scrollPrev->setGeometry(0, canPrev ? 0 : -height(), scrollButtonWidth, height());
202 m_scrollNext->setArrowType(Qt::RightArrow);
203 m_scrollNext->setGeometry(width() - scrollButtonWidth, canNext ? 0 : -height(), scrollButtonWidth, height());
204 } else {
205 // Right-to-left is mirrored.
206 m_scrollPrev->setArrowType(Qt::RightArrow);
207 m_scrollPrev->setGeometry(width() - scrollButtonWidth, canPrev ? 0 : -height(), scrollButtonWidth, height());
208 m_scrollNext->setArrowType(Qt::LeftArrow);
209 m_scrollNext->setGeometry(0, canNext ? 0 : -height(), scrollButtonWidth, height());
210 }
211 }
212
214 Qt::Orientation m_orientation;
215
216 QToolButton *m_scrollPrev;
217 QToolButton *m_scrollNext;
218};
219
220#endif // KO_TOOLBOX_SCROLL_AREA_H
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
int heightForWidth(int width) const override
int widthForHeight(int height) const
QSize sizeHint() const override
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.
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)