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

0001 //////////////////////////////////////////////////////////////////////////////
0002 // oxygentoolbardata.cpp
0003 // stores event filters and maps widgets to timelines for animations
0004 // -------------------
0005 //
0006 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007 //
0008 // SPDX-License-Identifier: MIT
0009 //////////////////////////////////////////////////////////////////////////////
0010 
0011 #include "oxygentoolbardata.h"
0012 
0013 #include <QToolButton>
0014 
0015 namespace Oxygen
0016 {
0017 //________________________________________________________________________
0018 ToolBarData::ToolBarData(QObject *parent, QWidget *target, int duration)
0019     : AnimationData(parent, target)
0020     , _opacity(0)
0021     , _progress(0)
0022     , _currentObject(nullptr)
0023     , _entered(false)
0024 {
0025     target->installEventFilter(this);
0026 
0027     _animation = new Animation(duration, this);
0028     animation().data()->setDirection(Animation::Forward);
0029     animation().data()->setStartValue(0.0);
0030     animation().data()->setEndValue(1.0);
0031     animation().data()->setTargetObject(this);
0032     animation().data()->setPropertyName("opacity");
0033 
0034     // progress animation
0035     _progressAnimation = new Animation(duration, this);
0036     progressAnimation().data()->setDirection(Animation::Forward);
0037     progressAnimation().data()->setStartValue(0);
0038     progressAnimation().data()->setEndValue(1);
0039     progressAnimation().data()->setTargetObject(this);
0040     progressAnimation().data()->setPropertyName("progress");
0041     progressAnimation().data()->setEasingCurve(QEasingCurve::Linear);
0042 
0043     // add all children widgets to event handler
0044     const auto children = target->children();
0045     for (QObject *child : children) {
0046         if (qobject_cast<QToolButton *>(child))
0047             childAddedEvent(child);
0048     }
0049 }
0050 
0051 //______________________________________________
0052 bool ToolBarData::eventFilter(QObject *object, QEvent *event)
0053 {
0054     // check object
0055     const QObject *targetData = target().data();
0056     if (object == targetData) {
0057         switch (event->type()) {
0058         case QEvent::Enter: {
0059             if (enabled()) {
0060                 object->event(event);
0061                 enterEvent(object);
0062                 return true;
0063             } else
0064                 return false;
0065         }
0066 
0067         case QEvent::ChildAdded: {
0068             // add children even in disabled case, to make sure they
0069             // are properly registered when engine is enabled
0070             QChildEvent *childEvent(static_cast<QChildEvent *>(event));
0071             childAddedEvent(childEvent->child());
0072             break;
0073         }
0074 
0075         default:
0076             break;
0077         }
0078 
0079     } else if (object->parent() == targetData) {
0080         if (!enabled())
0081             return false;
0082 
0083         switch (event->type()) {
0084         case QEvent::HoverEnter:
0085             childEnterEvent(object);
0086             break;
0087 
0088         case QEvent::HoverLeave:
0089             if (currentObject() && !_timer.isActive())
0090                 _timer.start(100, this);
0091             break;
0092 
0093         default:
0094             break;
0095         }
0096     }
0097 
0098     return false;
0099 }
0100 
0101 //____________________________________________________________
0102 void ToolBarData::updateAnimatedRect(void)
0103 {
0104     // check rect validity
0105     if (currentRect().isNull() || previousRect().isNull()) {
0106         _animatedRect = QRect();
0107         return;
0108     }
0109 
0110     // compute rect located 'between' previous and current
0111     _animatedRect.setLeft(previousRect().left() + progress() * (currentRect().left() - previousRect().left()));
0112     _animatedRect.setRight(previousRect().right() + progress() * (currentRect().right() - previousRect().right()));
0113     _animatedRect.setTop(previousRect().top() + progress() * (currentRect().top() - previousRect().top()));
0114     _animatedRect.setBottom(previousRect().bottom() + progress() * (currentRect().bottom() - previousRect().bottom()));
0115 
0116     // trigger update
0117     setDirty();
0118     return;
0119 }
0120 
0121 //________________________________________________________________________
0122 void ToolBarData::enterEvent(const QObject *)
0123 {
0124     if (_timer.isActive())
0125         _timer.stop();
0126     if (animation().data()->isRunning())
0127         animation().data()->stop();
0128     if (progressAnimation().data()->isRunning())
0129         progressAnimation().data()->stop();
0130     clearPreviousRect();
0131     clearAnimatedRect();
0132 
0133     return;
0134 }
0135 
0136 //________________________________________________________________________
0137 void ToolBarData::leaveEvent(const QObject *)
0138 {
0139     if (progressAnimation().data()->isRunning())
0140         progressAnimation().data()->stop();
0141     if (animation().data()->isRunning())
0142         animation().data()->stop();
0143     clearAnimatedRect();
0144     clearPreviousRect();
0145 
0146     if (currentObject()) {
0147         clearCurrentObject();
0148         animation().data()->setDirection(Animation::Backward);
0149         animation().data()->start();
0150     }
0151 
0152     return;
0153 }
0154 
0155 //________________________________________________________________________
0156 void ToolBarData::childEnterEvent(const QObject *object)
0157 {
0158     if (object == currentObject())
0159         return;
0160 
0161     const QToolButton *local = qobject_cast<const QToolButton *>(object);
0162 
0163     // check if current position match another action
0164     if (local && local->isEnabled()) {
0165         if (_timer.isActive())
0166             _timer.stop();
0167 
0168         // get rect
0169         QRect activeRect(local->rect().translated(local->mapToParent(QPoint(0, 0))));
0170 
0171         // update previous rect if the current action is valid
0172         if (currentObject()) {
0173             if (!progressAnimation().data()->isRunning()) {
0174                 setPreviousRect(currentRect());
0175 
0176             } else if (progress() < 1 && currentRect().isValid() && previousRect().isValid()) {
0177                 // re-calculate previous rect so that animatedRect
0178                 // is unchanged after currentRect is updated
0179                 // this prevents from having jumps in the animation
0180                 qreal ratio = progress() / (1.0 - progress());
0181                 _previousRect.adjust(ratio * (currentRect().left() - activeRect.left()),
0182                                      ratio * (currentRect().top() - activeRect.top()),
0183                                      ratio * (currentRect().right() - activeRect.right()),
0184                                      ratio * (currentRect().bottom() - activeRect.bottom()));
0185             }
0186 
0187             // update current action
0188             setCurrentObject(local);
0189             setCurrentRect(activeRect);
0190             if (animation().data()->isRunning())
0191                 animation().data()->stop();
0192             if (!progressAnimation().data()->isRunning())
0193                 progressAnimation().data()->start();
0194 
0195         } else {
0196             setCurrentObject(local);
0197             setCurrentRect(activeRect);
0198             if (!_entered) {
0199                 _entered = true;
0200                 if (animation().data()->isRunning())
0201                     animation().data()->stop();
0202                 if (!progressAnimation().data()->isRunning())
0203                     progressAnimation().data()->start();
0204 
0205             } else {
0206                 setPreviousRect(activeRect);
0207                 clearAnimatedRect();
0208                 if (progressAnimation().data()->isRunning())
0209                     progressAnimation().data()->stop();
0210                 animation().data()->setDirection(Animation::Forward);
0211                 if (!animation().data()->isRunning())
0212                     animation().data()->start();
0213             }
0214         }
0215 
0216     } else if (currentObject()) {
0217         if (!_timer.isActive())
0218             _timer.start(100, this);
0219     }
0220 
0221     return;
0222 }
0223 
0224 //___________________________________________________________________
0225 void ToolBarData::childAddedEvent(QObject *object)
0226 {
0227     QWidget *widget(qobject_cast<QWidget *>(object));
0228     if (!widget)
0229         return;
0230 
0231     // add connections
0232     connect(animation().data(), SIGNAL(valueChanged(QVariant)), widget, SLOT(update()), Qt::UniqueConnection);
0233     connect(progressAnimation().data(), SIGNAL(valueChanged(QVariant)), widget, SLOT(update()), Qt::UniqueConnection);
0234 
0235     // add event filter
0236     widget->removeEventFilter(this);
0237     widget->installEventFilter(this);
0238 }
0239 
0240 //___________________________________________________________
0241 void ToolBarData::timerEvent(QTimerEvent *event)
0242 {
0243     if (event->timerId() != _timer.timerId())
0244         return AnimationData::timerEvent(event);
0245     _timer.stop();
0246     leaveEvent(target().data());
0247 }
0248 }