File indexing completed on 2024-05-19 12:55:43

0001 /* This file is part of the KDE project
0002    Copyright (C) 2016 Jarosław Staniek <staniek@kde.org>
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 #include "KexiWidgetWidthAnimator.h"
0021 
0022 #include <kexiutils/utils.h>
0023 
0024 #include <QAbstractScrollArea>
0025 #include <QLayout>
0026 #include <QPainter>
0027 #include <QPaintEvent>
0028 #include <QSplitter>
0029 #include <QPropertyAnimation>
0030 #include <QDebug>
0031 
0032 const int PROJECT_NAVIGATOR_INDEX = 0;
0033 const int MAIN_AREA_INDEX = 1;
0034 const int PROPERTY_EDITOR_INDEX = 2;
0035 
0036 class KexiWidgetWidthAnimator::Private
0037 {
0038 public:
0039     explicit Private(KexiWidgetWidthAnimator *qq)
0040         : q(qq)
0041         , widthAnimation(0)
0042         , originalWidth(0)
0043         , frozen(false)
0044     {
0045     }
0046 
0047     void setSubWidgetsVisible(bool set)
0048     {
0049         QWidget *targetWidget = qobject_cast<QWidget*>(q->parent());
0050         QLayout *lyr = targetWidget->layout();
0051         if (!lyr) {
0052             return;
0053         }
0054         for (int i = 0; i < lyr->count(); ++i) {
0055             QWidget *subWidget = lyr->itemAt(i)->widget();
0056             if (subWidget) {
0057                 subWidget->setVisible(set);
0058             }
0059         }
0060     }
0061 
0062     void setSubWidgetsScrollbarsVisible(bool set)
0063     {
0064         QWidget *targetWidget = qobject_cast<QWidget*>(q->parent());
0065         QLayout *lyr = targetWidget->layout();
0066         if (!lyr) {
0067             return;
0068         }
0069         for (int i = 0; i < lyr->count(); ++i) {
0070             QAbstractScrollArea* area = qobject_cast<QAbstractScrollArea*>(lyr->itemAt(i)->widget());
0071             if (area) {
0072                 //! @todo remember and restore these settings instead of hardcoding!
0073                 area->setHorizontalScrollBarPolicy(set ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
0074                 area->setVerticalScrollBarPolicy(set ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
0075             }
0076         }
0077     }
0078 
0079     KexiWidgetWidthAnimator * const q;
0080     QPropertyAnimation *widthAnimation;
0081     int originalWidth;
0082     bool frozen;
0083     QPixmap snapshot;
0084 };
0085 
0086 KexiWidgetWidthAnimator::KexiWidgetWidthAnimator(QWidget *targetWidget)
0087  : QObject(targetWidget), d(new Private(this))
0088 {
0089     targetWidget->installEventFilter(this);
0090 }
0091 
0092 KexiWidgetWidthAnimator::~KexiWidgetWidthAnimator()
0093 {
0094     delete d;
0095 }
0096 
0097 void KexiWidgetWidthAnimator::setVisible(bool set)
0098 {
0099     const bool stoppedBeforeFinishing = d->widthAnimation && d->widthAnimation->state() == QAbstractAnimation::Running;
0100     QWidget *targetWidget = qobject_cast<QWidget*>(parent());
0101     if (set == targetWidget->isVisible()) {
0102         return;
0103     }
0104     if (!stoppedBeforeFinishing && !set) {
0105         d->originalWidth = width();
0106     }
0107     if (!d->widthAnimation) {
0108         d->widthAnimation = new QPropertyAnimation(this, "width");
0109         const int duration = (KexiUtils::graphicEffectsLevel() & KexiUtils::SimpleAnimationEffects) ? 300 : 0;
0110         d->widthAnimation->setDuration(duration);
0111         d->widthAnimation->setEasingCurve(QEasingCurve::InOutQuad);
0112         connect(d->widthAnimation, &QPropertyAnimation::finished,
0113                 this, &KexiWidgetWidthAnimator::slotWidthAnimationFinished);
0114     }
0115     if (stoppedBeforeFinishing) {
0116         d->widthAnimation->pause();
0117         const QVariant end = d->widthAnimation->endValue();
0118         d->widthAnimation->setEndValue(d->widthAnimation->startValue());
0119         d->widthAnimation->setStartValue(end);
0120         d->widthAnimation->setCurrentTime(
0121                 d->widthAnimation->duration() - d->widthAnimation->currentTime());
0122         d->widthAnimation->resume();
0123     } else {
0124         d->widthAnimation->setStartValue(set ? 0 : d->originalWidth);
0125         d->widthAnimation->setEndValue(set ? d->originalWidth : 0);
0126         //qDebug() << "targetWidget->isVisible():" << set << targetWidget->isVisible() << d->originalWidth << width();
0127         const int w = d->originalWidth;
0128         if (set) {
0129             targetWidget->setVisible(true);
0130             d->originalWidth = w; // restore because setVisible() causes resize event and changes d->originalWidth
0131         }
0132         if (d->widthAnimation->duration() > 0) {
0133             d->setSubWidgetsVisible(true);
0134             d->frozen = false;
0135             const QSize currentSize = targetWidget->size();
0136             //qDebug() << "currentSize:" << currentSize << d->originalWidth;
0137             targetWidget->resize(d->originalWidth, targetWidget->height());
0138             d->setSubWidgetsScrollbarsVisible(false);
0139             targetWidget->updateGeometry();
0140             d->snapshot = targetWidget->grab();
0141             targetWidget->resize(currentSize);
0142             d->originalWidth = w; // restore because targetWidget->resize() causes resize event and changes d->originalWidth
0143             d->setSubWidgetsScrollbarsVisible(true);
0144             d->setSubWidgetsVisible(false);
0145             d->frozen = true;
0146         }
0147         d->widthAnimation->start();
0148     }
0149 }
0150 
0151 int KexiWidgetWidthAnimator::width() const
0152 {
0153     return qobject_cast<QWidget*>(parent())->width();
0154 }
0155 
0156 void KexiWidgetWidthAnimator::setWidth(int width)
0157 {
0158     QWidget *targetWidget = qobject_cast<QWidget*>(parent());
0159     QSplitter* splitter = qobject_cast<QSplitter*>(targetWidget->parentWidget());
0160     const int index = splitter->indexOf(targetWidget);
0161     if (index < 0) {
0162         return;
0163     }
0164     QList<int> sizes(splitter->sizes());
0165     //qDebug() << "setWidth-" << sizes;
0166     int oldWidth = sizes[index];
0167     if (sizes.count() >= (MAIN_AREA_INDEX+1)) { // make total size unchanged
0168         sizes[MAIN_AREA_INDEX] += (oldWidth - width);
0169     }
0170     sizes[index] = width;
0171     targetWidget->resize(width, targetWidget->height());
0172     splitter->setSizes(sizes);
0173     //qDebug() << "setWidth" << index << width << sizes << splitter->sizes();
0174 }
0175 
0176 void KexiWidgetWidthAnimator::slotWidthAnimationFinished()
0177 {
0178     QWidget *targetWidget = qobject_cast<QWidget*>(parent());
0179     if (width() == 0) {
0180         targetWidget->setVisible(false);
0181     }
0182     d->frozen = false;
0183     d->snapshot = QPixmap(); // no longer needed
0184     d->setSubWidgetsVisible(true);
0185     emit animationFinished(targetWidget->isVisible());
0186 }
0187 
0188 bool KexiWidgetWidthAnimator::eventFilter(QObject *obj, QEvent *event)
0189 {
0190     if (obj == parent()) {
0191         switch (event->type()) {
0192         case QEvent::Paint:
0193             if (d->frozen) {
0194                 //qDebug() << "d->snapshot.size():" << d->snapshot.size();
0195                 QPaintEvent *paintEvent = static_cast<QPaintEvent*>(event);
0196                 QWidget *targetWidget = qobject_cast<QWidget*>(parent());
0197                 QPainter p(targetWidget);
0198                 p.drawPixmap(paintEvent->rect(), d->snapshot,
0199                              QRect(d->originalWidth - targetWidget->width(), 0,
0200                                    targetWidget->width(), targetWidget->height()));
0201             }
0202             break;
0203         case QEvent::Resize:
0204             if (!d->widthAnimation || d->widthAnimation->state() != QAbstractAnimation::Running) {
0205                 QResizeEvent *resizeEvent = static_cast<QResizeEvent*>(event);
0206                 d->originalWidth = resizeEvent->size().width();
0207                 //qDebug() << "new size:" << resizeEvent->size();
0208             }
0209             break;
0210         default:;
0211         }
0212     }
0213     return QObject::eventFilter(obj, event);
0214 }