File indexing completed on 2024-05-19 05:35:26

0001 //////////////////////////////////////////////////////////////////////////////
0002 // oxygentransitionwidget.cpp
0003 // stores event filters and maps widgets to transitions for transitions
0004 // -------------------
0005 //
0006 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007 //
0008 // SPDX-License-Identifier: MIT
0009 //////////////////////////////////////////////////////////////////////////////
0010 
0011 #include "oxygentransitionwidget.h"
0012 
0013 #include <QPaintEvent>
0014 #include <QPainter>
0015 #include <QStyleOption>
0016 #include <QTextStream>
0017 
0018 namespace Oxygen
0019 {
0020 //________________________________________________
0021 bool TransitionWidget::_paintEnabled = true;
0022 bool TransitionWidget::paintEnabled(void)
0023 {
0024     return _paintEnabled;
0025 }
0026 
0027 int TransitionWidget::_steps = 0;
0028 
0029 //________________________________________________
0030 TransitionWidget::TransitionWidget(QWidget *parent, int duration)
0031     : QWidget(parent)
0032     , _animation(new Animation(duration, this))
0033 {
0034     // background flags
0035     setAttribute(Qt::WA_NoSystemBackground);
0036     setAutoFillBackground(false);
0037 
0038     // setup animation
0039     _animation.data()->setStartValue(0);
0040     _animation.data()->setEndValue(1.0);
0041     _animation.data()->setTargetObject(this);
0042     _animation.data()->setPropertyName("opacity");
0043 
0044     // hide when animation is finished
0045     connect(_animation.data(), SIGNAL(finished()), SLOT(hide()));
0046 }
0047 
0048 //________________________________________________
0049 QPixmap TransitionWidget::grab(QWidget *widget, QRect rect)
0050 {
0051     // change rect
0052     if (!rect.isValid())
0053         rect = widget->rect();
0054     if (!rect.isValid())
0055         return QPixmap();
0056 
0057     // initialize pixmap
0058     QPixmap out(rect.size());
0059     out.fill(Qt::transparent);
0060     _paintEnabled = false;
0061 
0062     if (testFlag(GrabFromWindow)) {
0063         rect = rect.translated(widget->mapTo(widget->window(), widget->rect().topLeft()));
0064         widget = widget->window();
0065         out = widget->grab(rect);
0066 
0067     } else {
0068         if (!testFlag(Transparent)) {
0069             grabBackground(out, widget, rect);
0070         }
0071         grabWidget(out, widget, rect);
0072     }
0073 
0074     _paintEnabled = true;
0075 
0076     return out;
0077 }
0078 
0079 //________________________________________________
0080 bool TransitionWidget::event(QEvent *event)
0081 {
0082     switch (event->type()) {
0083     case QEvent::MouseButtonPress:
0084     case QEvent::MouseButtonRelease:
0085     case QEvent::KeyPress:
0086     case QEvent::KeyRelease:
0087         endAnimation();
0088         hide();
0089         event->ignore();
0090         return false;
0091 
0092     default:
0093         return QWidget::event(event);
0094     }
0095 }
0096 
0097 //________________________________________________
0098 void TransitionWidget::paintEvent(QPaintEvent *event)
0099 {
0100     // fully transparent case
0101     if (opacity() >= 1.0 && endPixmap().isNull())
0102         return;
0103     if (!_paintEnabled)
0104         return;
0105 
0106     // get rect
0107     QRect rect = event->rect();
0108     if (!rect.isValid())
0109         rect = this->rect();
0110 
0111     // local pixmap
0112     const bool paintOnWidget(testFlag(PaintOnWidget) && !testFlag(Transparent));
0113     if (!paintOnWidget) {
0114         if (_currentPixmap.isNull() || _currentPixmap.size() != size()) {
0115             _currentPixmap = QPixmap(size());
0116         }
0117     }
0118 
0119     // fill
0120     _currentPixmap.fill(Qt::transparent);
0121 
0122     // copy local pixmap to current
0123     {
0124         QPainter p;
0125 
0126         // draw end pixmap first, provided that opacity is small enough
0127         if (opacity() >= 0.004 && !_endPixmap.isNull()) {
0128             // faded endPixmap if parent target is transparent and opacity is
0129             if (opacity() <= 0.996 && testFlag(Transparent)) {
0130                 fade(_endPixmap, _currentPixmap, opacity(), rect);
0131                 p.begin(&_currentPixmap);
0132                 p.setClipRect(event->rect());
0133 
0134             } else {
0135                 if (paintOnWidget)
0136                     p.begin(this);
0137                 else
0138                     p.begin(&_currentPixmap);
0139                 p.setClipRect(event->rect());
0140                 p.drawPixmap(QPoint(), _endPixmap);
0141             }
0142 
0143         } else {
0144             if (paintOnWidget)
0145                 p.begin(this);
0146             else
0147                 p.begin(&_currentPixmap);
0148             p.setClipRect(event->rect());
0149         }
0150 
0151         // draw fading start pixmap
0152         if (opacity() <= 0.996 && !_startPixmap.isNull()) {
0153             if (opacity() >= 0.004) {
0154                 fade(_startPixmap, _localStartPixmap, 1.0 - opacity(), rect);
0155                 p.drawPixmap(QPoint(), _localStartPixmap);
0156 
0157             } else
0158                 p.drawPixmap(QPoint(), _startPixmap);
0159         }
0160 
0161         p.end();
0162     }
0163 
0164     // copy current pixmap on widget
0165     if (!paintOnWidget) {
0166         QPainter p(this);
0167         p.setClipRect(event->rect());
0168         p.drawPixmap(QPoint(0, 0), _currentPixmap);
0169         p.end();
0170     }
0171 }
0172 
0173 //________________________________________________
0174 void TransitionWidget::grabBackground(QPixmap &pixmap, QWidget *widget, QRect &rect) const
0175 {
0176     if (!widget)
0177         return;
0178 
0179     QWidgetList widgets;
0180     if (widget->autoFillBackground()) {
0181         widgets.append(widget);
0182     }
0183 
0184     QWidget *parent(nullptr);
0185 
0186     // get highest level parent
0187     for (parent = widget->parentWidget(); parent; parent = parent->parentWidget()) {
0188         if (!(parent->isVisible() && parent->rect().isValid()))
0189             continue;
0190 
0191         // store in list
0192         widgets.append(parent);
0193 
0194         // stop at topLevel
0195         if (parent->isTopLevel() || parent->autoFillBackground())
0196             break;
0197     }
0198 
0199     if (!parent)
0200         parent = widget;
0201 
0202     // painting
0203     QPainter p(&pixmap);
0204     p.setClipRect(rect);
0205     const QBrush backgroundBrush = parent->palette().brush(parent->backgroundRole());
0206     if (backgroundBrush.style() == Qt::TexturePattern) {
0207         p.drawTiledPixmap(rect, backgroundBrush.texture(), widget->mapTo(parent, rect.topLeft()));
0208 
0209     } else {
0210         p.fillRect(pixmap.rect(), backgroundBrush);
0211     }
0212 
0213     if (parent->isTopLevel() && parent->testAttribute(Qt::WA_StyledBackground)) {
0214         QStyleOption option;
0215         option.initFrom(parent);
0216         option.rect = rect;
0217         option.rect.translate(widget->mapTo(parent, rect.topLeft()));
0218         p.translate(-option.rect.topLeft());
0219         parent->style()->drawPrimitive(QStyle::PE_Widget, &option, &p, parent);
0220         p.translate(option.rect.topLeft());
0221     }
0222 
0223     // draw all widgets in parent list
0224     // backward
0225     QPaintEvent event(rect);
0226     for (int i = widgets.size() - 1; i >= 0; i--) {
0227         QWidget *w = widgets.at(i);
0228         w->render(&p, -widget->mapTo(w, rect.topLeft()), rect, {});
0229     }
0230 
0231     // end
0232     p.end();
0233 }
0234 
0235 //________________________________________________
0236 void TransitionWidget::grabWidget(QPixmap &pixmap, QWidget *widget, QRect &rect) const
0237 {
0238     widget->render(&pixmap, pixmap.rect().topLeft(), rect, QWidget::DrawChildren);
0239 }
0240 
0241 //________________________________________________
0242 void TransitionWidget::fade(const QPixmap &source, QPixmap &target, qreal opacity, const QRect &rect) const
0243 {
0244     if (target.isNull() || target.size() != size()) {
0245         target = QPixmap(size());
0246     }
0247 
0248     // erase target
0249     target.fill(Qt::transparent);
0250 
0251     // check opacity
0252     if (opacity * 255 < 1)
0253         return;
0254 
0255     QPainter p(&target);
0256     p.setClipRect(rect);
0257 
0258     // draw pixmap
0259     p.drawPixmap(QPoint(0, 0), source);
0260 
0261     // opacity mask (0.996 corresponds to 254/255)
0262     if (opacity <= 0.996) {
0263         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
0264         QColor color(Qt::black);
0265         color.setAlphaF(opacity);
0266         p.fillRect(rect, color);
0267     }
0268 
0269     p.end();
0270     return;
0271 }
0272 }