File indexing completed on 2024-05-05 04:43:05

0001 /***************************************************************************
0002  *   Copyright (C) 2018 by Emmanuel Lepage Vallee                          *
0003  *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@kde.org>             *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 3 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0017  **************************************************************************/
0018 #include "indexmetadata_p.h"
0019 
0020 // Qt
0021 #include <QQmlContext>
0022 
0023 //
0024 #include <private/statetracker/viewitem_p.h>
0025 #include <private/geostrategyselector_p.h>
0026 #include "adapters/contextadapter.h"
0027 #include "adapters/modeladapter.h"
0028 #include "viewport.h"
0029 #include "viewport_p.h"
0030 #include "contextadapterfactory.h"
0031 #include "statetracker/content_p.h"
0032 #include "proxies/sizehintproxymodel.h"
0033 #include "statetracker/geometry_p.h"
0034 #include "statetracker/proximity_p.h"
0035 #include "statetracker/index_p.h"
0036 #include "statetracker/selection_p.h"
0037 #include "statetracker/modelitem_p.h"
0038 
0039 class IndexMetadataPrivate
0040 {
0041 public:
0042     explicit IndexMetadataPrivate(IndexMetadata *q) : q_ptr(q) {}
0043 
0044     StateTracker::Geometry   m_GeoTracker        {         };
0045     StateTracker::ViewItem  *m_pViewTracker      { nullptr };
0046     StateTracker::Index     *m_pIndexTracker     { nullptr };
0047     StateTracker::ModelItem *m_pModelTracker     { nullptr };
0048     StateTracker::Proximity *m_pProximityTracker { nullptr };
0049     StateTracker::Selection *m_pSelectionTracker { nullptr };
0050     ViewItemContextAdapter  *m_pContextAdapter   { nullptr };
0051     Viewport                *m_pViewport         { nullptr };
0052 
0053     // Attributes
0054     bool m_IsCollapsed {false}; //TODO change the default to true
0055 
0056     typedef bool(IndexMetadataPrivate::*StateF)();
0057     static const IndexMetadataPrivate::StateF m_fStateMachine[5][7];
0058 
0059     bool nothing() {return true;}
0060     bool query  () {q_ptr->sizeHint(); return false;};
0061 
0062     IndexMetadata *q_ptr;
0063 };
0064 
0065 /**
0066  * Extend the class for the need of needs of the AbstractItemAdapter.
0067  */
0068 class ViewItemContextAdapter final : public ContextAdapter
0069 {
0070 public:
0071     explicit ViewItemContextAdapter(QQmlContext* p) : ContextAdapter(p){}
0072     virtual ~ViewItemContextAdapter() {
0073         flushCache();
0074     }
0075 
0076     virtual QModelIndex          index  () const override;
0077     virtual AbstractItemAdapter *item   () const override;
0078 
0079     IndexMetadata* m_pGeometry;
0080 };
0081 
0082 #define A &IndexMetadataPrivate::
0083 const IndexMetadataPrivate::StateF IndexMetadataPrivate::m_fStateMachine[5][7] = {
0084 /*                 MOVE      RESIZE      PLACE     RESET      MODIFY    DECORATE     VIEW  */
0085 /*INIT     */ { A nothing, A nothing, A nothing, A nothing, A query  , A nothing, A nothing},
0086 /*SIZE     */ { A nothing, A nothing, A nothing, A nothing, A nothing, A nothing, A nothing},
0087 /*POSITION */ { A nothing, A nothing, A nothing, A nothing, A nothing, A nothing, A nothing},
0088 /*PENDING  */ { A nothing, A nothing, A nothing, A nothing, A nothing, A nothing, A nothing},
0089 /*VALID    */ { A nothing, A nothing, A nothing, A nothing, A nothing, A nothing, A nothing},
0090 };
0091 #undef A
0092 
0093 IndexMetadata::IndexMetadata(StateTracker::Index *idxT, Viewport *p) :
0094     d_ptr(new IndexMetadataPrivate(this))
0095 {
0096     Q_ASSERT(idxT);
0097     d_ptr->m_pIndexTracker     = idxT;
0098     d_ptr->m_pModelTracker     = (StateTracker::ModelItem*) idxT;
0099     d_ptr->m_pViewport         = p;
0100     d_ptr->m_pProximityTracker = new StateTracker::Proximity(
0101         this, indexTracker()
0102     );
0103 }
0104 
0105 IndexMetadata::~IndexMetadata()
0106 {
0107     if (d_ptr->m_pContextAdapter) {
0108         if (d_ptr->m_pContextAdapter->isActive() && d_ptr->m_pContextAdapter->context())
0109             d_ptr->m_pContextAdapter->context()->setContextObject(nullptr);
0110         delete d_ptr->m_pContextAdapter;
0111     }
0112 
0113     delete d_ptr;
0114 }
0115 
0116 void IndexMetadata::setViewTracker(StateTracker::ViewItem *i)
0117 {
0118     if (d_ptr->m_pViewTracker && !i) {
0119         auto old = d_ptr->m_pViewTracker;
0120         d_ptr->m_pViewTracker = nullptr;
0121         d_ptr->m_pViewport->s_ptr->notifyRemoval(old->m_pMetadata);
0122     }
0123 
0124     if ((d_ptr->m_pViewTracker = i)) {
0125         i->m_pMetadata = this;
0126 
0127         // Assign the context object
0128         contextAdapter()->context();
0129     }
0130 }
0131 
0132 StateTracker::ViewItem *IndexMetadata::viewTracker() const
0133 {
0134     return d_ptr->m_pViewTracker;
0135 }
0136 
0137 StateTracker::Index *IndexMetadata::indexTracker() const
0138 {
0139     return d_ptr->m_pIndexTracker;
0140 }
0141 
0142 StateTracker::ModelItem *IndexMetadata::modelTracker() const
0143 {
0144     return d_ptr->m_pModelTracker;
0145 }
0146 
0147 StateTracker::Geometry *IndexMetadata::geometryTracker() const
0148 {
0149     return &d_ptr->m_GeoTracker;
0150 }
0151 
0152 StateTracker::Proximity *IndexMetadata::proximityTracker() const
0153 {
0154     return d_ptr->m_pProximityTracker;
0155 }
0156 
0157 StateTracker::Selection *IndexMetadata::selectionTracker() const
0158 {
0159     if (!d_ptr->m_pSelectionTracker)
0160         d_ptr->m_pSelectionTracker = new StateTracker::Selection();
0161 
0162     return d_ptr->m_pSelectionTracker;
0163 }
0164 
0165 QRectF IndexMetadata::decoratedGeometry() const
0166 {
0167     switch(d_ptr->m_GeoTracker.state()) {
0168         case StateTracker::Geometry::State::VALID:
0169         case StateTracker::Geometry::State::PENDING:
0170             break;
0171         case StateTracker::Geometry::State::INIT:
0172             Q_ASSERT(false);
0173             break;
0174         case StateTracker::Geometry::State::SIZE:
0175         case StateTracker::Geometry::State::POSITION:
0176             const_cast<IndexMetadata*>(this)->sizeHint();
0177     }
0178 
0179     const auto ret = d_ptr->m_GeoTracker.decoratedGeometry();
0180 
0181     Q_ASSERT(d_ptr->m_GeoTracker.state() == StateTracker::Geometry::State::VALID);
0182 
0183     return ret;
0184 }
0185 
0186 ContextAdapter* IndexMetadata::contextAdapter() const
0187 {
0188     if (!d_ptr->m_pContextAdapter) {
0189         auto cm = d_ptr->m_pViewport->modelAdapter()->contextAdapterFactory();
0190         d_ptr->m_pContextAdapter = cm->createAdapter<ViewItemContextAdapter>(
0191             d_ptr->m_pViewport->modelAdapter()->view()->rootContext()
0192         );
0193         d_ptr->m_pContextAdapter->m_pGeometry = const_cast<IndexMetadata*>(this);
0194 
0195         // This will init the context now.
0196         contextAdapter()->context();
0197     }
0198 
0199     return d_ptr->m_pContextAdapter;
0200 }
0201 
0202 QModelIndex ViewItemContextAdapter::index() const
0203 {
0204     return m_pGeometry->index();
0205 }
0206 
0207 AbstractItemAdapter* ViewItemContextAdapter::item() const
0208 {
0209     return m_pGeometry->viewTracker() ? m_pGeometry->viewTracker()->d_ptr : nullptr;
0210 }
0211 
0212 bool IndexMetadata::isValid() const
0213 {
0214     return d_ptr->m_GeoTracker.state() == StateTracker::Geometry::State::VALID ||
0215         d_ptr->m_GeoTracker.state() == StateTracker::Geometry::State::PENDING;
0216 }
0217 
0218 QSizeF IndexMetadata::sizeHint()
0219 {
0220     QSizeF ret;
0221 
0222     switch(d_ptr->m_GeoTracker.state()) {
0223         case StateTracker::Geometry::State::VALID:
0224         case StateTracker::Geometry::State::PENDING:
0225             break;
0226 
0227         case StateTracker::Geometry::State::POSITION:
0228         case StateTracker::Geometry::State::INIT: {
0229             Q_ASSERT(viewTracker());
0230 
0231             const auto ret = d_ptr->m_pViewport->s_ptr->m_pGeoAdapter->sizeHint(
0232                 index(),
0233                 viewTracker() ? viewTracker()->d_ptr : nullptr
0234             );
0235 
0236             d_ptr->m_GeoTracker.setSize(ret);
0237 
0238             break;
0239         }
0240         case StateTracker::Geometry::State::SIZE:
0241             break;
0242     }
0243 
0244     auto s = d_ptr->m_GeoTracker.state();
0245     Q_ASSERT(s != StateTracker::Geometry::State::INIT);
0246     Q_ASSERT(s != StateTracker::Geometry::State::POSITION);
0247 
0248     if (s == StateTracker::Geometry::State::SIZE) {
0249         if (auto prev = up()) {
0250 
0251             // A word of warning, this is recursive
0252             const auto prevGeo = prev->decoratedGeometry();
0253             Q_ASSERT(prevGeo.y() != -1);
0254             d_ptr->m_GeoTracker.setPosition(QPointF(0.0, prevGeo.y() + prevGeo.height()));
0255         }
0256         else if (isTopItem()) {
0257             d_ptr->m_GeoTracker.setPosition(QPointF(0.0, 0.0));
0258             Q_ASSERT(d_ptr->m_GeoTracker.state() == StateTracker::Geometry::State::PENDING);
0259         }
0260     }
0261 
0262     Q_ASSERT(isValid());
0263 
0264     return ret;
0265 }
0266 
0267 bool IndexMetadata::performAction(IndexMetadata::ViewAction a)
0268 {
0269     Q_ASSERT(viewTracker());
0270     if (!viewTracker())
0271         return false;
0272 
0273     return viewTracker()->performAction(a);
0274 }
0275 
0276 bool IndexMetadata::performAction(GeometryAction a)
0277 {
0278     const auto s = (int)d_ptr->m_GeoTracker.state();
0279 
0280     if ((d_ptr->*IndexMetadataPrivate::m_fStateMachine[s][(int)a])()) {
0281         d_ptr->m_GeoTracker.performAction(a);
0282         return true;
0283     }
0284 
0285     return false;
0286 }
0287 
0288 bool IndexMetadata::performAction(ProximityAction a, Qt::Edge e)
0289 {
0290     proximityTracker()->performAction(a, e);
0291     return true;
0292 }
0293 
0294 qreal IndexMetadata::borderDecoration(Qt::Edge e) const
0295 {
0296     return d_ptr->m_GeoTracker.borderDecoration(e);
0297 }
0298 
0299 void IndexMetadata::setBorderDecoration(Qt::Edge e, qreal r)
0300 {
0301     d_ptr->m_GeoTracker.setBorderDecoration(e, r);
0302 }
0303 
0304 void IndexMetadata::setSize(const QSizeF& s)
0305 {
0306     d_ptr->m_GeoTracker.setSize(s);
0307 }
0308 
0309 void IndexMetadata::setPosition(const QPointF& p)
0310 {
0311     d_ptr->m_GeoTracker.setPosition(p);
0312 }
0313 
0314 bool IndexMetadata::isInSync() const
0315 {
0316     switch(d_ptr->m_GeoTracker.state()) {
0317         case StateTracker::Geometry::State::VALID:
0318         case StateTracker::Geometry::State::PENDING:
0319             break;
0320         default:
0321             return false;
0322     }
0323 
0324     const auto item = viewTracker()->item();
0325 
0326     // If it got that far, then it's probably not going to load, trying to fix
0327     // that should be done elsewhere. Lets just assume a null delegate.
0328     if (!item) {
0329         return true;
0330     }
0331 
0332     const auto geo = d_ptr->m_GeoTracker.contentGeometry();
0333 
0334     // The actual QQuickItem position adjusted for the decoration
0335     QRectF correctedRect(
0336         item->x(),
0337         item->y(),
0338         item->width(),
0339         item->height()
0340     );
0341 
0342 //DEBUG
0343 //     qDebug() << "EXPECT" << geo << d_ptr->m_GeoTracker.borderDecoration(Qt::TopEdge);
0344 //     qDebug() << "GOT   " << correctedRect;
0345 
0346     return correctedRect == geo;
0347 }
0348 
0349 IndexMetadata *IndexMetadata::up() const
0350 {
0351     auto i = indexTracker()->up();
0352     return i ? i->metadata() : nullptr;
0353 }
0354 
0355 IndexMetadata *IndexMetadata::down() const
0356 {
0357     auto i = indexTracker()->down();
0358     return i ? i->metadata() : nullptr;
0359 }
0360 
0361 IndexMetadata *IndexMetadata::left() const
0362 {
0363     auto i = indexTracker()->left();
0364     return i ? i->metadata() : nullptr;
0365 }
0366 
0367 IndexMetadata *IndexMetadata::right() const
0368 {
0369     auto i = indexTracker()->right();
0370     return i ? i->metadata() : nullptr;
0371 }
0372 
0373 IndexMetadata *IndexMetadata::next(Qt::Edge e) const
0374 {
0375     auto i = indexTracker()->next(e);
0376     return i ? i->metadata() : nullptr;
0377 }
0378 
0379 bool IndexMetadata::isCollapsed() const
0380 {
0381     return d_ptr->m_IsCollapsed;
0382 }
0383 
0384 void IndexMetadata::setCollapsed(bool c)
0385 {
0386     d_ptr->m_IsCollapsed = c;
0387 }
0388 
0389 bool IndexMetadata::performAction(IndexMetadata::LoadAction a)
0390 {
0391     auto mt = static_cast<StateTracker::ModelItem*>(indexTracker());
0392 
0393     Q_ASSERT(mt->state() != StateTracker::ModelItem::State::ERROR);
0394     const auto s  = mt->state();
0395     const auto ns = mt->m_State = mt->m_fStateMap[(int)s][(int)a];
0396     Q_ASSERT(mt->state() != StateTracker::ModelItem::State::ERROR);
0397 
0398     // This need to be done before calling the transition function because it
0399     // can trigger another round of state change.
0400     if (s != mt->state() && modelTracker()->q_ptr) {
0401         //Note that modelTracker()->q_ptr is nullptr if this == root
0402 
0403         const auto r = modelTracker()->q_ptr;
0404         r->perfromStateChange(StateTracker::Content::Event::LEAVE_STATE, this, s);
0405         r->perfromStateChange(StateTracker::Content::Event::ENTER_STATE, this, s);
0406     }
0407 
0408     // At this point the edges should have been updated (or deleted).
0409     if (mt->q_ptr) { _DO_TEST(_test_validate_edges_simple, mt->q_ptr) }
0410 
0411     const bool ret = (mt->*StateTracker::ModelItem::m_fStateMachine[(int)s][(int)a])();
0412 
0413     //WARNING, do not access mt form here, it might have been deleted
0414 
0415     Q_ASSERT(ns == StateTracker::ModelItem::State::DANGLING ||
0416         ((!mt->metadata()->viewTracker())
0417         ||  mt->state() == StateTracker::ModelItem::State::BUFFER
0418         ||  mt->state() == StateTracker::ModelItem::State::MOVING
0419         ||  mt->state() == StateTracker::ModelItem::State::VISIBLE));
0420 
0421     //mt->q_ptr will be nullptr during a reset
0422     if (ns != StateTracker::ModelItem::State::DANGLING && mt->q_ptr) {
0423         _DO_TEST(_test_validate_edges_simple, mt->q_ptr)
0424     }
0425 
0426     return ret;
0427 }
0428 
0429 bool IndexMetadata::isVisible() const
0430 {
0431     return modelTracker()->state() ==
0432         StateTracker::ModelItem::State::VISIBLE;
0433 }
0434 
0435 QModelIndex IndexMetadata::index() const
0436 {
0437     return QModelIndex(indexTracker()->index());
0438 }
0439 
0440 bool IndexMetadata::isTopItem() const
0441 {
0442     return indexTracker() == modelTracker()->q_ptr->firstItem();
0443 }
0444 
0445 Viewport *IndexMetadata::viewport() const
0446 {
0447     return d_ptr->m_pViewport;
0448 }