File indexing completed on 2024-05-05 04:43:04
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 "geostrategyselector_p.h" 0019 0020 // Qt 0021 #include <QtCore/QAbstractItemModel> 0022 #include <QtCore/QDebug> 0023 0024 // KQuickItemViews 0025 #include <viewport.h> 0026 #include <proxies/sizehintproxymodel.h> 0027 #include <strategies/justintime.h> 0028 #include <strategies/proxy.h> 0029 #include <strategies/role.h> 0030 #include <strategies/delegate.h> 0031 #include <strategies/aheadoftime.h> 0032 #include <strategies/uniform.h> 0033 0034 0035 class GeoStrategySelectorPrivate : public QObject 0036 { 0037 public: 0038 explicit GeoStrategySelectorPrivate(GeoStrategySelector *q) : QObject(q), q_ptr(q) {} 0039 0040 enum class BuiltInStrategies { 0041 AOT , /*!< Load everything ahead of time, doesn't scale but very reliable */ 0042 JIT , /*!< Do not try to compute the total size, scrollbars wont work */ 0043 UNIFORM , /*!< Assume all elements have the same size, scales well when true */ 0044 PROXY , /*!< Use a QSizeHintProxyModel, require work by all developers */ 0045 ROLE , /*!< Use one of the QAbstractItemModel role as size */ 0046 DELEGATE, /*!< Assume the view re-implemented ::sizeHint is correct */ 0047 }; 0048 0049 BuiltInStrategies m_CurrentStrategy { BuiltInStrategies::JIT }; 0050 0051 GeometryAdapter *m_A {nullptr}; 0052 QAbstractItemModel *m_pModel {nullptr}; 0053 bool m_Auto { true }; 0054 0055 uint m_Features {GeoStrategySelector::Features::NONE}; 0056 0057 bool checkHasContent(); 0058 bool checkHasRole (); 0059 bool checkProxyModel(); 0060 0061 void optimize(); 0062 0063 void replaceStrategy(BuiltInStrategies s); 0064 0065 GeoStrategySelector *q_ptr; 0066 0067 public Q_SLOTS: 0068 void slotRowsInserted(); 0069 }; 0070 0071 GeoStrategySelector::GeoStrategySelector(Viewport *parent) : GeometryAdapter(parent), 0072 d_ptr(new GeoStrategySelectorPrivate(this)) 0073 { 0074 d_ptr->m_A = new GeometryStrategies::JustInTime(parent); 0075 } 0076 0077 GeoStrategySelector::~GeoStrategySelector() 0078 {} 0079 0080 QSizeF GeoStrategySelector::sizeHint(const QModelIndex& i, AbstractItemAdapter *a) const 0081 { 0082 return d_ptr->m_A ? 0083 d_ptr->m_A->sizeHint(i, a) : GeometryAdapter::sizeHint(i, a); 0084 } 0085 0086 QPointF GeoStrategySelector::positionHint(const QModelIndex& i, AbstractItemAdapter *a) const 0087 { 0088 if (d_ptr->m_A && d_ptr->m_A->capabilities() & Capabilities::HAS_POSITION_HINTS) 0089 return d_ptr->m_A->positionHint(i, a); 0090 0091 //TODO implement the fallback 0092 return GeometryAdapter::positionHint(i, a); 0093 } 0094 0095 int GeoStrategySelector::capabilities() const 0096 { 0097 return d_ptr->m_A ? 0098 d_ptr->m_A->capabilities() : GeometryAdapter::capabilities(); 0099 } 0100 0101 bool GeoStrategySelector::isSizeForced() const 0102 { 0103 return d_ptr->m_A ? 0104 d_ptr->m_A->isSizeForced() : GeometryAdapter::isSizeForced(); 0105 } 0106 0107 void GeoStrategySelector::setSizeForced(bool f) 0108 { 0109 GeometryAdapter::setSizeForced(f); 0110 0111 if (d_ptr->m_A) 0112 d_ptr->m_A->setSizeForced(f); 0113 } 0114 0115 void GeoStrategySelector::setModel(QAbstractItemModel *m) 0116 { 0117 if (m == d_ptr->m_pModel) 0118 return; 0119 0120 d_ptr->m_pModel = m; 0121 0122 static constexpr auto toClean = Features::HAS_MODEL 0123 | Features::HAS_SHP_MODEL 0124 | Features::HAS_MODEL_CONTENT 0125 | Features::HAS_SIZE_ROLE; 0126 0127 d_ptr->m_Features = d_ptr->m_Features & (~toClean); 0128 0129 // Reload the model-dependent features 0130 d_ptr->m_Features |= m ? 0131 Features::HAS_MODEL : Features::NONE; 0132 d_ptr->m_Features |= d_ptr->checkHasContent() ? 0133 Features::HAS_MODEL_CONTENT : Features::NONE; 0134 d_ptr->m_Features |= d_ptr->checkHasRole() ? 0135 Features::HAS_SIZE_ROLE : Features::NONE; 0136 d_ptr->m_Features |= d_ptr->checkProxyModel() ? 0137 Features::HAS_SHP_MODEL : Features::NONE; 0138 0139 d_ptr->optimize(); 0140 } 0141 0142 void GeoStrategySelector::setHasScrollbar(bool v) 0143 { 0144 d_ptr->m_Features = d_ptr->m_Features & (~Features::HAS_SCROLLBAR); 0145 d_ptr->m_Features |= v ? Features::HAS_SCROLLBAR : Features::NONE; 0146 } 0147 0148 void GeoStrategySelectorPrivate::slotRowsInserted() 0149 { 0150 checkHasRole(); 0151 0152 // Now that the introspection is done, there is no further need for this 0153 disconnect(m_pModel, &QAbstractItemModel::rowsInserted, 0154 this, &GeoStrategySelectorPrivate::slotRowsInserted); 0155 } 0156 0157 bool GeoStrategySelectorPrivate::checkHasContent() 0158 { 0159 if (!m_pModel) 0160 return false; 0161 0162 return m_pModel->index(0, 0).isValid(); 0163 } 0164 0165 bool GeoStrategySelectorPrivate::checkHasRole() 0166 { 0167 if (!m_pModel) 0168 return false; 0169 0170 const QModelIndex i = m_pModel->index(0, 0); 0171 0172 return i.data(Qt::SizeHintRole).isValid(); 0173 } 0174 0175 bool GeoStrategySelectorPrivate::checkProxyModel() 0176 { 0177 return m_pModel && m_pModel->metaObject()->inherits( 0178 &SizeHintProxyModel::staticMetaObject 0179 ); 0180 } 0181 0182 void GeoStrategySelectorPrivate::optimize() 0183 { 0184 if (!m_Auto) 0185 return; 0186 0187 // Here will eventually reside the main optimization algorithm. For now 0188 // just choose between the JustInTime and Proxy adapters, the only ones 0189 // fully implemented. 0190 0191 BuiltInStrategies next = m_CurrentStrategy; 0192 0193 if (m_Features & GeoStrategySelector::Features::HAS_SHP_MODEL) { 0194 next = BuiltInStrategies::PROXY; 0195 } 0196 else { 0197 next = BuiltInStrategies::JIT; 0198 } 0199 0200 if (next != m_CurrentStrategy) { 0201 replaceStrategy(next); 0202 } 0203 } 0204 0205 void GeoStrategySelectorPrivate::replaceStrategy(BuiltInStrategies s) 0206 { 0207 delete m_A; 0208 m_A = nullptr; 0209 0210 switch(s) { 0211 case BuiltInStrategies::AOT: 0212 //TODO this requires ViewportAdapter to work 0213 break; 0214 case BuiltInStrategies::JIT: 0215 m_A = new GeometryStrategies::JustInTime(q_ptr->viewport()); 0216 break; 0217 case BuiltInStrategies::UNIFORM: 0218 m_A = new GeometryStrategies::Uniform(q_ptr->viewport()); 0219 break; 0220 case BuiltInStrategies::PROXY: 0221 m_A = new GeometryStrategies::Proxy(q_ptr->viewport()); 0222 break; 0223 case BuiltInStrategies::ROLE: 0224 m_A = new GeometryStrategies::Role(q_ptr->viewport()); 0225 break; 0226 case BuiltInStrategies::DELEGATE: 0227 m_A = new GeometryStrategies::Delegate(q_ptr->viewport()); 0228 break; 0229 } 0230 0231 emit q_ptr->dismissResult(); 0232 } 0233 0234 bool GeoStrategySelector::isAutomatic() const 0235 { 0236 return d_ptr->m_Auto; 0237 } 0238 0239 GeometryAdapter *GeoStrategySelector::currentAdapter() const 0240 { 0241 return d_ptr->m_A; 0242 } 0243 0244 void GeoStrategySelector::setCurrentAdapter(GeometryAdapter *a) 0245 { 0246 if (a == d_ptr->m_A) 0247 return; 0248 0249 if (d_ptr->m_Auto || a->parent() == viewport()) 0250 delete d_ptr->m_A; 0251 0252 d_ptr->m_Auto = !a; 0253 0254 d_ptr->m_A = a; 0255 0256 if (d_ptr->m_Auto) 0257 d_ptr->optimize(); 0258 0259 // Mitigate a race condition when setSizeForced is called before setCurrentAdapter 0260 // due to the undefined property order in QML. 0261 if (d_ptr->m_A && GeometryAdapter::isSizeForced()) 0262 d_ptr->m_A->setSizeForced(true); 0263 0264 Q_ASSERT(d_ptr->m_A); 0265 0266 emit dismissResult(); 0267 }