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 }