File indexing completed on 2024-05-19 05:28:49

0001 //////////////////////////////////////////////////////////////////////////////
0002 // breezetransitionwidget.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 "breezetransitionwidget.h"
0012 
0013 #include <QPaintEvent>
0014 #include <QPainter>
0015 #include <QStyleOption>
0016 #include <QTextStream>
0017 
0018 namespace Breeze
0019 {
0020 //________________________________________________
0021 bool TransitionWidget::_paintEnabled = true;
0022 bool TransitionWidget::paintEnabled()
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(), &QAbstractAnimation::finished, this, &QWidget::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     }
0055     if (!rect.isValid()) {
0056         return QPixmap();
0057     }
0058 
0059     // initialize pixmap
0060     QPixmap out(rect.size());
0061     out.fill(Qt::transparent);
0062     _paintEnabled = false;
0063 
0064     if (testFlag(GrabFromWindow)) {
0065         rect = rect.translated(widget->mapTo(widget->window(), widget->rect().topLeft()));
0066         widget = widget->window();
0067         out = widget->grab(rect);
0068 
0069     } else {
0070         if (!testFlag(Transparent)) {
0071             grabBackground(out, widget, rect);
0072         }
0073         grabWidget(out, widget, rect);
0074     }
0075 
0076     _paintEnabled = true;
0077 
0078     return out;
0079 }
0080 
0081 //________________________________________________
0082 bool TransitionWidget::event(QEvent *event)
0083 {
0084     switch (event->type()) {
0085     case QEvent::MouseButtonPress:
0086     case QEvent::MouseButtonRelease:
0087     case QEvent::KeyPress:
0088     case QEvent::KeyRelease:
0089         endAnimation();
0090         hide();
0091         event->ignore();
0092         return false;
0093 
0094     default:
0095         return QWidget::event(event);
0096     }
0097 }
0098 
0099 //________________________________________________
0100 void TransitionWidget::paintEvent(QPaintEvent *event)
0101 {
0102     // fully transparent case
0103     if (opacity() >= 1.0 && endPixmap().isNull()) {
0104         return;
0105     }
0106     if (!_paintEnabled) {
0107         return;
0108     }
0109 
0110     // get rect
0111     QRect rect = event->rect();
0112     if (!rect.isValid()) {
0113         rect = this->rect();
0114     }
0115 
0116     // local pixmap
0117     const bool paintOnWidget(testFlag(PaintOnWidget) && !testFlag(Transparent));
0118     if (!paintOnWidget) {
0119         if (_currentPixmap.isNull() || _currentPixmap.size() != size()) {
0120             _currentPixmap = QPixmap(size());
0121         }
0122     }
0123 
0124     // fill
0125     _currentPixmap.fill(Qt::transparent);
0126 
0127     // copy local pixmap to current
0128     {
0129         QPainter p;
0130 
0131         // draw end pixmap first, provided that opacity is small enough
0132         if (opacity() >= 0.004 && !_endPixmap.isNull()) {
0133             // faded endPixmap if parent target is transparent and opacity is
0134             if (opacity() <= 0.996 && testFlag(Transparent)) {
0135                 fade(_endPixmap, _currentPixmap, opacity(), rect);
0136                 p.begin(&_currentPixmap);
0137                 p.setClipRect(event->rect());
0138 
0139             } else {
0140                 if (paintOnWidget) {
0141                     p.begin(this);
0142                 } else {
0143                     p.begin(&_currentPixmap);
0144                 }
0145                 p.setClipRect(event->rect());
0146                 p.drawPixmap(QPoint(), _endPixmap);
0147             }
0148 
0149         } else {
0150             if (paintOnWidget) {
0151                 p.begin(this);
0152             } else {
0153                 p.begin(&_currentPixmap);
0154             }
0155             p.setClipRect(event->rect());
0156         }
0157 
0158         // draw fading start pixmap
0159         if (opacity() <= 0.996 && !_startPixmap.isNull()) {
0160             if (opacity() >= 0.004) {
0161                 fade(_startPixmap, _localStartPixmap, 1.0 - opacity(), rect);
0162                 p.drawPixmap(QPoint(), _localStartPixmap);
0163 
0164             } else {
0165                 p.drawPixmap(QPoint(), _startPixmap);
0166             }
0167         }
0168 
0169         p.end();
0170     }
0171 
0172     // copy current pixmap on widget
0173     if (!paintOnWidget) {
0174         QPainter p(this);
0175         p.setClipRect(event->rect());
0176         p.drawPixmap(QPoint(0, 0), _currentPixmap);
0177         p.end();
0178     }
0179 }
0180 
0181 //________________________________________________
0182 void TransitionWidget::grabBackground(QPixmap &pixmap, QWidget *widget, QRect &rect) const
0183 {
0184     if (!widget) {
0185         return;
0186     }
0187 
0188     QWidgetList widgets;
0189     if (widget->autoFillBackground()) {
0190         widgets.append(widget);
0191     }
0192 
0193     QWidget *parent(nullptr);
0194 
0195     // get highest level parent
0196     for (parent = widget->parentWidget(); parent; parent = parent->parentWidget()) {
0197         if (!(parent->isVisible() && parent->rect().isValid())) {
0198             continue;
0199         }
0200 
0201         // store in list
0202         widgets.append(parent);
0203 
0204         // stop at topLevel
0205         if (parent->isWindow() || parent->autoFillBackground()) {
0206             break;
0207         }
0208     }
0209 
0210     if (!parent) {
0211         parent = widget;
0212     }
0213 
0214     // painting
0215     QPainter p(&pixmap);
0216     p.setClipRect(rect);
0217     const QBrush backgroundBrush = parent->palette().brush(parent->backgroundRole());
0218     if (backgroundBrush.style() == Qt::TexturePattern) {
0219         p.drawTiledPixmap(rect, backgroundBrush.texture(), widget->mapTo(parent, rect.topLeft()));
0220 
0221     } else {
0222         p.fillRect(pixmap.rect(), backgroundBrush);
0223     }
0224 
0225     if (parent->isWindow() && parent->testAttribute(Qt::WA_StyledBackground)) {
0226         QStyleOption option;
0227         option.initFrom(parent);
0228         option.rect = rect;
0229         option.rect.translate(widget->mapTo(parent, rect.topLeft()));
0230         p.translate(-option.rect.topLeft());
0231         parent->style()->drawPrimitive(QStyle::PE_Widget, &option, &p, parent);
0232         p.translate(option.rect.topLeft());
0233     }
0234 
0235     // draw all widgets in parent list
0236     // backward
0237     QPaintEvent event(rect);
0238     for (int i = widgets.size() - 1; i >= 0; i--) {
0239         QWidget *w = widgets.at(i);
0240         w->render(&p, -widget->mapTo(w, rect.topLeft()), rect, {});
0241     }
0242 
0243     // end
0244     p.end();
0245 }
0246 
0247 //________________________________________________
0248 void TransitionWidget::grabWidget(QPixmap &pixmap, QWidget *widget, QRect &rect) const
0249 {
0250     widget->render(&pixmap, pixmap.rect().topLeft(), rect, QWidget::DrawChildren);
0251 }
0252 
0253 //________________________________________________
0254 void TransitionWidget::fade(const QPixmap &source, QPixmap &target, qreal opacity, const QRect &rect) const
0255 {
0256     if (target.isNull() || target.size() != size()) {
0257         target = QPixmap(size());
0258     }
0259 
0260     // erase target
0261     target.fill(Qt::transparent);
0262 
0263     // check opacity
0264     if (opacity * 255 < 1) {
0265         return;
0266     }
0267 
0268     QPainter p(&target);
0269     p.setClipRect(rect);
0270 
0271     // draw pixmap
0272     p.drawPixmap(QPoint(0, 0), source);
0273 
0274     // opacity mask (0.996 corresponds to 254/255)
0275     if (opacity <= 0.996) {
0276         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
0277         QColor color(Qt::black);
0278         color.setAlphaF(opacity);
0279         p.fillRect(rect, color);
0280     }
0281 
0282     p.end();
0283 }
0284 
0285 }