File indexing completed on 2024-05-12 12:42:30
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 }