Warning, file /office/calligra/libs/widgets/KoToolBoxScrollArea_p.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * Copyright (c) 2018 Alvin Wong <alvinhochun@gmail.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #ifndef KO_TOOLBOX_SCROLL_AREA_H
0021 #define KO_TOOLBOX_SCROLL_AREA_H
0022 
0023 #include "KoToolBox_p.h"
0024 #include "KoToolBoxLayout_p.h"
0025 
0026 #include <QScrollArea>
0027 #include <QScrollBar>
0028 #include <QScroller>
0029 #include <QStyleOption>
0030 #include <QToolButton>
0031 #include <KoKineticScroller.h>
0032 
0033 class KoToolBoxScrollArea : public QScrollArea
0034 {
0035     Q_OBJECT
0036 public:
0037     KoToolBoxScrollArea(KoToolBox *toolBox, QWidget *parent)
0038         : QScrollArea(parent)
0039         , m_toolBox(toolBox)
0040         , m_orientation(Qt::Vertical)
0041         , m_scrollPrev(new QToolButton(this))
0042         , m_scrollNext(new QToolButton(this))
0043     {
0044         setFrameShape(QFrame::NoFrame);
0045         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0046         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0047 
0048         m_toolBox->setOrientation(m_orientation);
0049         setWidget(m_toolBox);
0050 
0051         m_scrollPrev->setAutoRepeat(true);
0052         m_scrollPrev->setAutoFillBackground(true);
0053         m_scrollPrev->setFocusPolicy(Qt::NoFocus);
0054         connect(m_scrollPrev, &QToolButton::clicked, this, &KoToolBoxScrollArea::doScrollPrev);
0055         m_scrollNext->setAutoRepeat(true);
0056         m_scrollNext->setAutoFillBackground(true);
0057         m_scrollNext->setFocusPolicy(Qt::NoFocus);
0058         connect(m_scrollNext, &QToolButton::clicked, this, &KoToolBoxScrollArea::doScrollNext);
0059 
0060         QScroller *scroller = KoKineticScroller::createPreconfiguredScroller(this);
0061         if (!scroller) {
0062             QScroller::grabGesture(viewport(), QScroller::MiddleMouseButtonGesture);
0063             QScroller *scroller = QScroller::scroller(viewport());
0064             QScrollerProperties sp = scroller->scrollerProperties();
0065 
0066             sp.setScrollMetric(QScrollerProperties::MaximumVelocity, 0.0);
0067             sp.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.1);
0068             sp.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.1);
0069             sp.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0);
0070             sp.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.4);
0071 
0072             scroller->setScrollerProperties(sp);
0073         }
0074         connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
0075     }
0076 
0077     void setOrientation(Qt::Orientation orientation)
0078     {
0079         if (orientation == m_orientation) {
0080             return;
0081         }
0082         m_orientation = orientation;
0083         m_toolBox->setOrientation(orientation);
0084         layoutItems();
0085     }
0086 
0087     Qt::Orientation orientation() const
0088     {
0089         return m_orientation;
0090     }
0091 
0092     QSize minimumSizeHint() const override
0093     {
0094         return m_toolBox->minimumSizeHint();
0095     }
0096 
0097     QSize sizeHint() const override
0098     {
0099         return m_toolBox->sizeHint();
0100     }
0101 
0102 public Q_SLOTS:
0103     void slotScrollerStateChange(QScroller::State state){ KoKineticScroller::updateCursor(this, state); }
0104 
0105 protected:
0106     bool event(QEvent *event) override
0107     {
0108         if (event->type() == QEvent::LayoutRequest) {
0109             // LayoutRequest can be triggered by icon changes, so resize the toolbox
0110             layoutItems();
0111             // The toolbox might have changed the sizeHint and minimumSizeHint
0112             updateGeometry();
0113         }
0114         return QScrollArea::event(event);
0115     }
0116 
0117     void resizeEvent(QResizeEvent *event) override
0118     {
0119         layoutItems();
0120         QScrollArea::resizeEvent(event);
0121         updateScrollButtons();
0122     }
0123 
0124     void wheelEvent(QWheelEvent *event) override
0125     {
0126         if (m_orientation == Qt::Vertical) {
0127             QApplication::sendEvent(verticalScrollBar(), event);
0128         } else {
0129             QApplication::sendEvent(horizontalScrollBar(), event);
0130         }
0131     }
0132 
0133     void scrollContentsBy(int dx, int dy) override
0134     {
0135         QScrollArea::scrollContentsBy(dx, dy);
0136         updateScrollButtons();
0137     }
0138 
0139 private Q_SLOTS:
0140     void doScrollPrev()
0141     {
0142         if (m_orientation == Qt::Vertical) {
0143             verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
0144         } else {
0145             horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
0146         }
0147     }
0148 
0149     void doScrollNext()
0150     {
0151         if (m_orientation == Qt::Vertical) {
0152             verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
0153         } else {
0154             horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
0155         }
0156     }
0157 
0158 private:
0159     int scrollButtonWidth() const
0160     {
0161         QStyleOption opt;
0162         opt.init(this);
0163         return style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, this);
0164     }
0165 
0166     void layoutItems()
0167     {
0168         const KoToolBoxLayout *l = m_toolBox->toolBoxLayout();
0169         QSize newSize = viewport()->size();
0170         if (m_orientation == Qt::Vertical) {
0171             newSize.setHeight(l->heightForWidth(newSize.width()));
0172         } else {
0173             newSize.setWidth(l->widthForHeight(newSize.height()));
0174         }
0175         m_toolBox->resize(newSize);
0176 
0177         updateScrollButtons();
0178     }
0179 
0180     void updateScrollButtons()
0181     {
0182         // We move the scroll buttons outside the widget rect instead of setting
0183         // the visibility, because setting the visibility triggers a relayout
0184         // of QAbstractScrollArea, which occasionally causes an offset bug when
0185         // QScroller performs the overshoot animation. (Overshoot is done by
0186         // moving the viewport widget, but the viewport position is reset during
0187         // a relayout.)
0188         const int scrollButtonWidth = this->scrollButtonWidth();
0189         if (m_orientation == Qt::Vertical) {
0190             m_scrollPrev->setArrowType(Qt::UpArrow);
0191             m_scrollPrev->setEnabled(verticalScrollBar()->value() != verticalScrollBar()->minimum());
0192             if (m_scrollPrev->isEnabled()) {
0193                 m_scrollPrev->setGeometry(0, 0, width(), scrollButtonWidth);
0194             } else {
0195                 m_scrollPrev->setGeometry(-width(), 0, width(), scrollButtonWidth);
0196             }
0197             m_scrollNext->setArrowType(Qt::DownArrow);
0198             m_scrollNext->setEnabled(verticalScrollBar()->value() != verticalScrollBar()->maximum());
0199             if (m_scrollNext->isEnabled()) {
0200                 m_scrollNext->setGeometry(0, height() - scrollButtonWidth, width(), scrollButtonWidth);
0201             } else {
0202                 m_scrollNext->setGeometry(-width(), height() - scrollButtonWidth, width(), scrollButtonWidth);
0203             }
0204         } else {
0205             m_scrollPrev->setArrowType(Qt::LeftArrow);
0206             m_scrollPrev->setEnabled(horizontalScrollBar()->value() != horizontalScrollBar()->minimum());
0207             if (m_scrollPrev->isEnabled()) {
0208                 m_scrollPrev->setGeometry(0, 0, scrollButtonWidth, height());
0209             } else {
0210                 m_scrollPrev->setGeometry(0, -height(), scrollButtonWidth, height());
0211             }
0212             m_scrollNext->setArrowType(Qt::RightArrow);
0213             m_scrollNext->setEnabled(horizontalScrollBar()->value() != horizontalScrollBar()->maximum());
0214             if (m_scrollNext->isEnabled()) {
0215                 m_scrollNext->setGeometry(width() - scrollButtonWidth, 0, scrollButtonWidth, height());
0216             } else {
0217                 m_scrollNext->setGeometry(width() - scrollButtonWidth, -height(), scrollButtonWidth, height());
0218             }
0219         }
0220     }
0221 
0222     KoToolBox *m_toolBox;
0223     Qt::Orientation m_orientation;
0224 
0225     QToolButton *m_scrollPrev;
0226     QToolButton *m_scrollNext;
0227 };
0228 
0229 #endif // KO_TOOLBOX_SCROLL_AREA_H