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 }