File indexing completed on 2024-05-12 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 "modelitem_p.h" 0019 0020 #include "proximity_p.h" 0021 #include <private/statetracker/content_p.h> 0022 #include <viewport.h> 0023 #include <private/viewport_p.h> 0024 #include <private/statetracker/viewitem_p.h> 0025 0026 #define S StateTracker::ModelItem::State:: 0027 const StateTracker::ModelItem::State StateTracker::ModelItem::m_fStateMap[8][8] = { 0028 /* SHOW HIDE ATTACH DETACH UPDATE MOVE RESET REPARENT */ 0029 /*NEW */ { S ERROR , S ERROR , S REACHABLE, S ERROR , S ERROR , S ERROR , S ERROR , S ERROR }, 0030 /*BUFFER */ { S VISIBLE, S BUFFER , S BUFFER , S DANGLING , S BUFFER , S BUFFER , S BUFFER , S MOVING }, 0031 /*REMOVED */ { S ERROR , S ERROR , S ERROR , S REACHABLE, S ERROR , S ERROR , S ERROR , S MOVING }, 0032 /*REACHABLE*/ { S VISIBLE, S REACHABLE, S ERROR , S REACHABLE, S ERROR , S REACHABLE, S REACHABLE, S MOVING }, 0033 /*VISIBLE */ { S VISIBLE, S BUFFER , S ERROR , S ERROR , S VISIBLE, S VISIBLE , S VISIBLE , S MOVING }, 0034 /*ERROR */ { S ERROR , S ERROR , S ERROR , S ERROR , S ERROR , S ERROR , S ERROR , S ERROR }, 0035 /*DANGLING */ { S ERROR , S ERROR , S ERROR , S ERROR , S ERROR , S ERROR , S ERROR , S ERROR }, 0036 /*MOVING */ { S VISIBLE, S ERROR , S ERROR , S ERROR , S ERROR , S VISIBLE , S ERROR , S BUFFER }, 0037 }; 0038 #undef S 0039 0040 #define A &StateTracker::ModelItem:: 0041 const StateTracker::ModelItem::StateF StateTracker::ModelItem::m_fStateMachine[8][8] = { 0042 /* SHOW HIDE ATTACH DETACH UPDATE MOVE RESET REPARENT */ 0043 /*NEW */ { A error , A nothing, A nothing, A error , A error , A error , A error, A error }, 0044 /*BUFFER */ { A show , A remove2, A attach , A destroy , A refresh, A move , A reset, A nothing }, 0045 /*REMOVED */ { A error , A error , A error , A detach , A error , A error , A reset, A nothing }, 0046 /*REACHABLE*/ { A show , A nothing, A error , A detach , A error , A move , A reset, A nothing }, 0047 /*VISIBLE */ { A nothing, A hide , A error , A error , A refresh, A move , A reset, A nothing }, 0048 /*ERROR */ { A error , A error , A error , A error , A error , A error , A error, A error }, 0049 /*DANGLING */ { A error , A error , A error , A error , A error , A error , A error, A error }, 0050 /*MOVING */ { A move , A error , A error , A error , A error , A nothing, A error, A nothing }, 0051 }; 0052 #undef A 0053 0054 0055 StateTracker::ModelItem::ModelItem(Viewport *v): 0056 StateTracker::Index(v), q_ptr(v->s_ptr->m_pReflector) 0057 {} 0058 0059 StateTracker::ModelItem* StateTracker::ModelItem::load(Qt::Edge e) const 0060 { 0061 // First, if it's already loaded, then don't bother 0062 if (auto ret = next(e)) 0063 return ret->metadata()->modelTracker(); 0064 0065 const auto t = metadata()->proximityTracker(); 0066 const QModelIndexList l = t->getNext(e); 0067 0068 // The consumer of this function only cares about the item above `this`, 0069 // not all the items that were loaded because they are dependencies. 0070 StateTracker::Index *i = nullptr; 0071 0072 for (const QModelIndex& idx : qAsConst(l)) { 0073 Q_ASSERT(idx.isValid()); 0074 q_ptr->forceInsert(idx); 0075 } 0076 0077 // This works because only the TopEdge and LeftEdge can return multiple `l` 0078 i = next(e); 0079 0080 _DO_TEST(_test_validateViewport, q_ptr); 0081 0082 return i ? i->metadata()->modelTracker() : nullptr; 0083 } 0084 0085 /** 0086 * When the item is moved, the old loading state is worthless. 0087 * 0088 * Recompute it based on the siblings. 0089 */ 0090 void StateTracker::ModelItem::rebuildState() 0091 { 0092 static const auto VISIBLE = StateTracker::ModelItem::State::VISIBLE; 0093 0094 //TODO make a 3D matrix out of this 0095 auto u = up () ? up ()->metadata()->modelTracker() : nullptr; 0096 auto d = down() ? down()->metadata()->modelTracker() : nullptr; 0097 0098 const bool betweenVis = u && d && u->state() == VISIBLE && d->state() == VISIBLE; 0099 const bool nearVisible = ((!u) && d && d->state() == VISIBLE) || 0100 ((!d) && u && u->state() == VISIBLE); 0101 0102 if (betweenVis || nearVisible) 0103 m_State = StateTracker::ModelItem::State::VISIBLE; 0104 else 0105 Q_ASSERT(false); //TODO not implemented 0106 } 0107 0108 IndexMetadata::EdgeType StateTracker::ModelItem::isTopEdge() const 0109 { 0110 //FIXME use the ModelItem cache 0111 0112 const auto r = metadata()->viewport()->s_ptr->m_pReflector; 0113 0114 if (r->getEdge(IndexMetadata::EdgeType::VISIBLE, Qt::TopEdge)) { 0115 return IndexMetadata::EdgeType::VISIBLE; 0116 } 0117 0118 if (r->getEdge(IndexMetadata::EdgeType::VISIBLE, Qt::TopEdge)) { 0119 return IndexMetadata::EdgeType::BUFFERED; 0120 } 0121 0122 return IndexMetadata::EdgeType::NONE; 0123 } 0124 0125 IndexMetadata::EdgeType StateTracker::ModelItem::isBottomEdge() const 0126 { 0127 //FIXME use the ModelItem cache 0128 0129 const auto r = metadata()->viewport()->s_ptr->m_pReflector; 0130 0131 if (r->getEdge(IndexMetadata::EdgeType::VISIBLE, Qt::BottomEdge)) { 0132 return IndexMetadata::EdgeType::VISIBLE; 0133 } 0134 0135 if (r->getEdge(IndexMetadata::EdgeType::VISIBLE, Qt::BottomEdge)) { 0136 return IndexMetadata::EdgeType::BUFFERED; 0137 } 0138 0139 return IndexMetadata::EdgeType::NONE; 0140 } 0141 0142 StateTracker::ModelItem::State StateTracker::ModelItem::state() const 0143 { 0144 return m_State; 0145 } 0146 0147 void StateTracker::ModelItem::remove(bool reparent) 0148 { 0149 // First, update the edges 0150 const auto te = isTopEdge(); 0151 const auto be = isBottomEdge(); 0152 0153 //TODO turn this into a state tracker, really 0154 switch(te) { 0155 case IndexMetadata::EdgeType::NONE: 0156 break; 0157 case IndexMetadata::EdgeType::FREE: 0158 Q_ASSERT(false); // Impossible, only corrupted memory can cause this 0159 break; 0160 case IndexMetadata::EdgeType::VISIBLE: 0161 if (auto i = q_ptr->find(this, Qt::TopEdge, [](auto i) -> bool { 0162 return i->metadata()->modelTracker()->m_State == StateTracker::ModelItem::State::VISIBLE; } )) { 0163 Q_ASSERT(i->metadata()->modelTracker()->m_State == StateTracker::ModelItem::State::VISIBLE); //TODO 0164 q_ptr->setEdge(IndexMetadata::EdgeType::VISIBLE, i, Qt::TopEdge); 0165 } 0166 else { 0167 Q_ASSERT(false); //TODO clear the edges 0168 } 0169 break; 0170 case IndexMetadata::EdgeType::BUFFERED: 0171 Q_ASSERT(false); //TODO as of the time of this comment, it's hald implemented 0172 break; 0173 } 0174 0175 switch(be) { 0176 case IndexMetadata::EdgeType::NONE: 0177 break; 0178 case IndexMetadata::EdgeType::FREE: 0179 Q_ASSERT(false); // Impossible, only corrupted memory can cause this 0180 break; 0181 case IndexMetadata::EdgeType::VISIBLE: 0182 if (auto i = q_ptr->find(this, Qt::BottomEdge, [](auto i) -> bool { 0183 return i->metadata()->modelTracker()->m_State == StateTracker::ModelItem::State::VISIBLE; } )) { 0184 Q_ASSERT(i->metadata()->modelTracker()->m_State == StateTracker::ModelItem::State::VISIBLE); //TODO 0185 q_ptr->setEdge(IndexMetadata::EdgeType::VISIBLE, i, Qt::BottomEdge); 0186 } 0187 else { 0188 Q_ASSERT(false); //TODO clear the edges 0189 } 0190 break; 0191 case IndexMetadata::EdgeType::BUFFERED: 0192 Q_ASSERT(false); //TODO as of the time of this comment, it's hald implemented 0193 break; 0194 } 0195 0196 metadata()->viewport()->s_ptr->notifyRemoval(metadata()); 0197 metadata() << IndexMetadata::LoadAction::REPARENT; 0198 Index::remove(reparent); 0199 } 0200 0201 bool StateTracker::ModelItem::nothing() 0202 { return true; } 0203 0204 #pragma GCC diagnostic push 0205 #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" 0206 bool StateTracker::ModelItem::error() 0207 { 0208 Q_ASSERT(false); 0209 return false; 0210 } 0211 #pragma GCC diagnostic pop 0212 0213 bool StateTracker::ModelItem::show() 0214 { 0215 Q_ASSERT(m_State == State::VISIBLE); 0216 0217 if (metadata()->viewTracker()) { 0218 //TODO Implementing this *may* make sense, but for now avoid it. 0219 // for now it is undefined behavior, but not fatal 0220 Q_ASSERT(false); //TODO not well thought, most definitely broken 0221 //item->setVisible(true); 0222 } 0223 else { 0224 Q_ASSERT(metadata()->viewport()->s_ptr->m_fFactory); 0225 metadata()->setViewTracker(metadata()->viewport()->s_ptr->m_fFactory()->s_ptr); 0226 Q_ASSERT(metadata()->viewTracker()); 0227 0228 metadata() << IndexMetadata::ViewAction::ATTACH; 0229 Q_ASSERT(metadata()->viewTracker()->state() == StateTracker::ViewItem::State::POOLED); 0230 Q_ASSERT(m_State == State::VISIBLE); 0231 0232 //DEBUG 0233 //if (auto item = metadata()->viewTracker()->item()) 0234 // qDebug() << "CREATE" << this << item->y() << item->height(); 0235 } 0236 0237 metadata() << IndexMetadata::ViewAction::ENTER_BUFFER; 0238 0239 // Make sure no `performAction` loop caused the item to get out of view 0240 Q_ASSERT(m_State == State::VISIBLE); 0241 0242 // When the delegate fails to load (or there is none), there is nothing to do 0243 if (metadata()->viewTracker()->state() == StateTracker::ViewItem::State::FAILED) 0244 return false; 0245 0246 Q_ASSERT(metadata()->viewTracker()->state() == StateTracker::ViewItem::State::BUFFER); 0247 0248 metadata() << IndexMetadata::ViewAction::ENTER_VIEW; 0249 0250 // Make sure no `performAction` loop caused the item to get out of view 0251 Q_ASSERT(m_State == State::VISIBLE); 0252 0253 metadata()->viewport()->s_ptr->updateGeometry(metadata()); 0254 0255 // For some reason creating the visual element failed, this can and will 0256 // happen and need to be recovered from. 0257 if (metadata()->viewTracker()->state() == ViewItem::State::FAILED) { 0258 metadata() << IndexMetadata::ViewAction::LEAVE_BUFFER; 0259 0260 // Make sure no `performAction` loop caused the item to get out of view 0261 Q_ASSERT(m_State == State::VISIBLE); 0262 0263 metadata()->setViewTracker(nullptr); 0264 } 0265 0266 //FIXME do this less often 0267 // This has to be done after `setViewTracker` and performAction 0268 if (down() && down()->metadata()->isValid()) 0269 metadata()->viewport()->s_ptr->notifyInsert(down()->metadata()); 0270 0271 // Make sure no `performAction` loop caused the item to get out of view 0272 Q_ASSERT(m_State == State::VISIBLE); 0273 0274 // Update the edges 0275 if (m_State == State::VISIBLE) { 0276 auto first = q_ptr->edges(IndexMetadata::EdgeType::VISIBLE)->getEdge(Qt::TopEdge); 0277 auto prev = up(); 0278 auto next = down(); 0279 auto last = q_ptr->edges(IndexMetadata::EdgeType::VISIBLE)->getEdge(Qt::BottomEdge); 0280 0281 // Make sure the geometry is up to data 0282 metadata()->decoratedGeometry(); 0283 Q_ASSERT(metadata()->isValid()); 0284 0285 if (first == next) { 0286 Q_ASSERT((!up()) || (up()->metadata()->modelTracker()->m_State != State::VISIBLE)); 0287 q_ptr->setEdge(IndexMetadata::EdgeType::VISIBLE, this, Qt::TopEdge); 0288 } 0289 0290 if (prev == last) { 0291 Q_ASSERT((!down()) || (down()->metadata()->modelTracker()->m_State != State::VISIBLE)); 0292 q_ptr->setEdge(IndexMetadata::EdgeType::VISIBLE, this, Qt::BottomEdge); 0293 } 0294 0295 } 0296 else 0297 Q_ASSERT(false); //TODO handle failed elements 0298 0299 //d_ptr->_test_validate_geometry_cache(); //TODO THIS_COMMIT 0300 //Q_ASSERT(metadata()->isInSync()); //TODO THIS_COMMIT 0301 0302 _DO_TEST(_test_validateViewport, q_ptr) 0303 0304 return true; 0305 } 0306 0307 bool StateTracker::ModelItem::hide() 0308 { 0309 if (!metadata()->viewTracker()) 0310 return true; 0311 0312 // This needs more reflection, is using "visible" instead of freeing 0313 // memory a good idea? 0314 //item->setVisible(false); 0315 metadata() << IndexMetadata::ViewAction::LEAVE_BUFFER; 0316 0317 return true; 0318 } 0319 0320 bool StateTracker::ModelItem::remove2() 0321 { 0322 if (!metadata()->viewTracker()) 0323 return true; 0324 0325 Q_ASSERT(metadata()->viewTracker()->state() != StateTracker::ViewItem::State::POOLED ); 0326 Q_ASSERT(metadata()->viewTracker()->state() != StateTracker::ViewItem::State::POOLING ); 0327 Q_ASSERT(metadata()->viewTracker()->state() != StateTracker::ViewItem::State::DANGLING); 0328 0329 // Move the item lifecycle forward 0330 while (metadata()->viewTracker()->state() != StateTracker::ViewItem::State::POOLED 0331 && metadata()->viewTracker()->state() != StateTracker::ViewItem::State::DANGLING) 0332 metadata() << IndexMetadata::ViewAction::DETACH; 0333 0334 // It should still exists, it may crash otherwise, so make sure early 0335 Q_ASSERT(metadata()->viewTracker()->state() == StateTracker::ViewItem::State::POOLED 0336 || metadata()->viewTracker()->state() == StateTracker::ViewItem::State::DANGLING 0337 ); 0338 0339 metadata()->setViewTracker(nullptr); 0340 0341 return true; 0342 } 0343 0344 bool StateTracker::ModelItem::attach() 0345 { 0346 Q_ASSERT(m_State != State::VISIBLE && !metadata()->viewTracker()); 0347 _DO_TEST(_test_validate_edges_simple, q_ptr) 0348 0349 //TODO For now the buffer isn't fully implemented, so items always get 0350 // shown when attached. 0351 0352 //FIXME this ain't correct 0353 return metadata() << IndexMetadata::LoadAction::SHOW; 0354 } 0355 0356 bool StateTracker::ModelItem::detach() 0357 { 0358 const auto children = allLoadedChildren(); 0359 0360 // First, detach any remaining children 0361 for (auto i : qAsConst(children)) { 0362 i->metadata() 0363 << IndexMetadata::LoadAction::HIDE 0364 << IndexMetadata::LoadAction::DETACH; 0365 } 0366 0367 remove2(); 0368 StateTracker::Index::remove(); 0369 0370 metadata()->viewport()->s_ptr->refreshVisible(); 0371 0372 Q_ASSERT(!loadedChildrenCount() && ((!parent()) || !parent()->childrenLookup(index()))); 0373 Q_ASSERT(!metadata()->viewTracker()); 0374 0375 return true; 0376 } 0377 0378 bool StateTracker::ModelItem::refresh() 0379 { 0380 metadata()->viewport()->s_ptr->updateGeometry(metadata()); 0381 0382 for (auto i = firstChild(); i; i = i->nextSibling()) { 0383 Q_ASSERT(i != this); 0384 i->metadata() << IndexMetadata::LoadAction::UPDATE; 0385 0386 if (i == lastChild()) 0387 break; 0388 } 0389 0390 // If this isn't true then the state machine is broken 0391 Q_ASSERT(metadata()->viewTracker()); 0392 0393 return true; 0394 } 0395 0396 bool StateTracker::ModelItem::move() 0397 { 0398 //FIXME Currently this is O(N^2) because refreshVisible does it too 0399 0400 // Propagate to all children 0401 for (auto i = firstChild(); i; i = i->nextSibling()) { 0402 Q_ASSERT(i != this); 0403 i->metadata() << IndexMetadata::LoadAction::MOVE; 0404 0405 if (i == lastChild()) 0406 break; 0407 } 0408 0409 if (metadata()->viewTracker()) { 0410 metadata() << IndexMetadata::ViewAction::MOVE; 0411 //Q_ASSERT(metadata()->isInSync());//TODO THIS_COMMIT 0412 } 0413 0414 Q_ASSERT(metadata()->isValid()); 0415 0416 return true; 0417 } 0418 0419 bool StateTracker::ModelItem::destroy() 0420 { 0421 Q_ASSERT((!q_ptr) || parent() || this == q_ptr->root()); 0422 Q_ASSERT((!parent()) || parent()->hasChildren(this)); 0423 0424 detach(); 0425 0426 Q_ASSERT((!metadata()->viewTracker()) && !loadedChildrenCount()); 0427 0428 delete this; 0429 return true; 0430 } 0431 0432 bool StateTracker::ModelItem::reset() 0433 { 0434 for (auto i = firstChild(); i; i = i->nextSibling()) { 0435 Q_ASSERT(i); 0436 Q_ASSERT(i != this); 0437 i->metadata() << IndexMetadata::LoadAction::RESET; 0438 0439 if (i == lastChild()) 0440 break; 0441 } 0442 0443 if (metadata()->viewTracker()) { 0444 metadata() 0445 << IndexMetadata::ViewAction::LEAVE_BUFFER; 0446 0447 // Move the item lifecycle forward 0448 while (metadata()->viewTracker()->state() != StateTracker::ViewItem::State::POOLED 0449 && metadata()->viewTracker()->state() != StateTracker::ViewItem::State::DANGLING) 0450 metadata() << IndexMetadata::ViewAction::DETACH; 0451 0452 metadata()->setViewTracker(nullptr); 0453 } 0454 0455 metadata() << IndexMetadata::GeometryAction::RESET; 0456 0457 return true; 0458 }