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 }