File indexing completed on 2024-11-10 05:11:08
0001 /* 0002 * Copyright 2018 by Marco Martin <mart@kde.org> 0003 * 0004 * Licensed under the Apache License, Version 2.0 (the "License"); 0005 * you may not use this file except in compliance with the License. 0006 * You may obtain a copy of the License at 0007 * 0008 * http://www.apache.org/licenses/LICENSE-2.0 0009 * 0010 * Unless required by applicable law or agreed to in writing, software 0011 * distributed under the License is distributed on an "AS IS" BASIS, 0012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0013 * See the License for the specific language governing permissions and 0014 * limitations under the License. 0015 * 0016 */ 0017 0018 #include "abstractdelegate.h" 0019 #include "mycroftcontroller.h" 0020 0021 #include <QQmlEngine> 0022 #include <QQmlContext> 0023 0024 0025 DelegateLoader::DelegateLoader(AbstractSkillView *parent) 0026 : QObject(parent), 0027 m_view(parent) 0028 {} 0029 0030 DelegateLoader::~DelegateLoader() 0031 { 0032 if (m_delegate) { 0033 m_delegate->deleteLater(); 0034 } 0035 } 0036 0037 void DelegateLoader::init(const QString skillId, const QUrl &delegateUrl) 0038 { 0039 if (!m_skillId.isEmpty()) { 0040 qWarning() << "Init already called"; 0041 } 0042 0043 m_skillId = skillId; 0044 m_delegateUrl = delegateUrl; 0045 QQmlEngine *engine = qmlEngine(m_view); 0046 //This class should be *ALWAYS* created from QML 0047 Q_ASSERT(engine); 0048 0049 m_component = new QQmlComponent(engine, delegateUrl, m_view); 0050 0051 switch(m_component->status()) { 0052 case QQmlComponent::Error: 0053 qWarning() << "ERROR Loading QML file" << delegateUrl; 0054 for (auto err : m_component->errors()) { 0055 qWarning() << err.toString(); 0056 } 0057 break; 0058 case QQmlComponent::Ready: 0059 createObject(); 0060 break; 0061 case QQmlComponent::Loading: 0062 connect(m_component, &QQmlComponent::statusChanged, this, &DelegateLoader::createObject); 0063 break; 0064 default: 0065 break; 0066 } 0067 } 0068 0069 QUrl DelegateLoader::translationsUrl() const 0070 { 0071 QUrl url(m_delegateUrl); 0072 url.setPath(m_delegateUrl.path().mid(0, m_delegateUrl.path().indexOf(QStringLiteral("/ui/")) + 4) + QStringLiteral("translations")); 0073 0074 return url; 0075 } 0076 0077 void DelegateLoader::createObject() 0078 { 0079 QQmlContext *context = QQmlEngine::contextForObject(m_view); 0080 //This class should be *ALWAYS* created from QML 0081 Q_ASSERT(context); 0082 0083 QObject *guiObject = m_component->beginCreate(context); 0084 m_delegate = qobject_cast<AbstractDelegate *>(guiObject); 0085 if (m_component->isError()) { 0086 qWarning() << "ERROR Loading QML file" << m_delegateUrl; 0087 for (auto err : m_component->errors()) { 0088 qWarning() << err.toString(); 0089 } 0090 return; 0091 } 0092 0093 if (!m_delegate) { 0094 qWarning()<<"ERROR: QML gui" << guiObject << "not a Mycroft.AbstractDelegate instance"; 0095 guiObject->deleteLater(); 0096 return; 0097 } 0098 0099 connect(m_delegate, &QObject::destroyed, this, &QObject::deleteLater); 0100 0101 m_delegate->setSkillId(m_skillId); 0102 m_delegate->setQmlUrl(m_delegateUrl); 0103 m_delegate->setSkillView(m_view); 0104 m_delegate->setSessionData(m_view->sessionDataForSkill(m_skillId)); 0105 m_component->completeCreate(); 0106 0107 emit delegateCreated(); 0108 0109 if (m_focus) { 0110 m_delegate->forceActiveFocus((Qt::FocusReason)AbstractSkillView::ServerEventFocusReason); 0111 } 0112 }; 0113 0114 AbstractDelegate *DelegateLoader::delegate() 0115 { 0116 return m_delegate; 0117 } 0118 0119 void DelegateLoader::setFocus(bool focus) 0120 { 0121 m_focus = focus; 0122 0123 if (m_delegate && focus) { 0124 m_delegate->forceActiveFocus((Qt::FocusReason)AbstractSkillView::ServerEventFocusReason); 0125 } else if (m_delegate) { 0126 m_delegate->setFocus(false); 0127 } 0128 } 0129 0130 ////////////////////////////////////////// 0131 0132 AbstractDelegate::AbstractDelegate(QQuickItem *parent) 0133 : QQuickItem(parent) 0134 { 0135 setFiltersChildMouseEvents(true); 0136 setFlags(QQuickItem::ItemIsFocusScope); 0137 setAcceptedMouseButtons(Qt::LeftButton); 0138 } 0139 0140 AbstractDelegate::~AbstractDelegate() 0141 { 0142 } 0143 0144 void AbstractDelegate::triggerGuiEvent(const QString &eventName, const QVariantMap ¶meters) 0145 { 0146 if (!m_skillView) { 0147 qWarning() << "No SkillView, this should never happen: orphan delegate?"; 0148 return; 0149 } 0150 0151 if (eventName.startsWith(QStringLiteral("system."))) { 0152 m_skillView->triggerEvent(QStringLiteral("system"), eventName, parameters); 0153 } else { 0154 m_skillView->triggerEvent(m_skillId, eventName, parameters); 0155 } 0156 } 0157 0158 void AbstractDelegate::syncChildItemsGeometry(const QSizeF &size) 0159 { 0160 if (m_contentItem) { 0161 m_contentItem->setX(m_leftPadding + m_leftInset); 0162 m_contentItem->setY(m_topPadding + m_topInset); 0163 if (m_contentItemAutoWidth && m_contentItemAutoHeight) { 0164 m_contentItem->setSize(QSizeF(size.width() - m_leftPadding - m_rightPadding - m_leftInset - m_rightInset, 0165 size.height() - m_topPadding - m_bottomPadding - m_topInset - m_bottomInset)); 0166 } else if (m_contentItemAutoWidth) { 0167 m_contentItem->setWidth(size.width() - m_leftPadding - m_rightPadding - m_leftInset - m_rightInset); 0168 } else if (m_contentItemAutoHeight) { 0169 m_contentItem->setHeight(size.height() - m_topPadding - m_bottomPadding - m_topInset - m_bottomInset); 0170 } 0171 } 0172 0173 if (m_backgroundItem) { 0174 m_backgroundItem->setX(m_leftInset); 0175 m_backgroundItem->setY(m_topInset); 0176 m_backgroundItem->setSize(size - QSizeF(m_leftInset + m_rightInset, m_topInset + m_bottomInset)); 0177 } 0178 0179 } 0180 0181 void AbstractDelegate::contentData_append(QQmlListProperty<QObject> *prop, QObject *object) 0182 { 0183 AbstractDelegate *delegate = static_cast<AbstractDelegate *>(prop->object); 0184 if (!delegate) { 0185 return; 0186 } 0187 0188 // QQuickItem *item = qobject_cast<QQuickItem *>(object); 0189 delegate->m_contentData.append(object); 0190 } 0191 0192 qsizetype AbstractDelegate::contentData_count(QQmlListProperty<QObject> *prop) 0193 { 0194 AbstractDelegate *delegate = static_cast<AbstractDelegate *>(prop->object); 0195 if (!delegate) { 0196 return 0; 0197 } 0198 0199 return delegate->m_contentData.count(); 0200 } 0201 0202 QObject *AbstractDelegate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index) 0203 { 0204 AbstractDelegate *delegate = static_cast<AbstractDelegate *>(prop->object); 0205 if (!delegate) { 0206 return nullptr; 0207 } 0208 0209 if (index < 0 || index >= delegate->m_contentData.count()) { 0210 return nullptr; 0211 } 0212 return delegate->m_contentData.value(index); 0213 } 0214 0215 void AbstractDelegate::contentData_clear(QQmlListProperty<QObject> *prop) 0216 { 0217 AbstractDelegate *delegate = static_cast<AbstractDelegate *>(prop->object); 0218 if (!delegate) { 0219 return; 0220 } 0221 0222 return delegate->m_contentData.clear(); 0223 } 0224 0225 QQmlListProperty<QObject> AbstractDelegate::contentData() 0226 { 0227 // return QQmlListProperty<QObject>(this, nullptr, 0228 // contentData_append, 0229 // contentData_count, 0230 // contentData_at, 0231 // contentData_clear); 0232 0233 // Need to convert this to be compatible with Qt 6 and not return nullptr 0234 return QQmlListProperty<QObject>(this, nullptr, contentData_append, contentData_count, contentData_at, contentData_clear); 0235 0236 } 0237 0238 void AbstractDelegate::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) 0239 { 0240 syncChildItemsGeometry(newGeometry.size()); 0241 QQuickItem::geometryChange(newGeometry, oldGeometry); 0242 emit contentWidthChanged(); 0243 emit contentHeightChanged(); 0244 } 0245 0246 void AbstractDelegate::componentComplete() 0247 { 0248 if (!m_contentItem) { 0249 //qWarning()<<"Creting default contentItem"; 0250 m_contentItem = new QQuickItem(this); 0251 } 0252 0253 QQuickItem *item; 0254 for (auto *o : m_contentData) { 0255 item = qobject_cast<QQuickItem *>(o); 0256 if (item) { 0257 item->setParentItem(m_contentItem); 0258 } else { 0259 o->setParent(this); 0260 } 0261 } 0262 QQuickItem::componentComplete(); 0263 } 0264 0265 bool AbstractDelegate::childMouseEventFilter(QQuickItem *item, QEvent *event) 0266 { 0267 if (event->type() == QEvent::MouseButtonPress) { 0268 forceActiveFocus(Qt::MouseFocusReason); 0269 triggerGuiEvent(QStringLiteral("system.gui.user.interaction"), QVariantMap({{QStringLiteral("skillId"), m_skillId}})); 0270 } 0271 return QQuickItem::childMouseEventFilter(item, event); 0272 } 0273 0274 void AbstractDelegate::mousePressEvent(QMouseEvent *event) 0275 { 0276 forceActiveFocus(Qt::MouseFocusReason); 0277 triggerGuiEvent(QStringLiteral("system.gui.user.interaction"), QVariantMap({{QStringLiteral("skillId"), m_skillId}})); 0278 } 0279 0280 void AbstractDelegate::keyReleaseEvent(QKeyEvent *event) 0281 { 0282 triggerGuiEvent(QStringLiteral("system.gui.user.interaction"), QVariantMap({{QStringLiteral("skillId"), m_skillId}})); 0283 } 0284 0285 void AbstractDelegate::focusInEvent(QFocusEvent *event) 0286 { 0287 //if the focus came from the server, don't ask the server again 0288 if (event->reason() == (Qt::FocusReason)AbstractSkillView::ServerEventFocusReason) { 0289 return; 0290 } 0291 0292 if (!parentItem()) { 0293 return; 0294 } 0295 0296 QQmlContext *context = QQmlEngine::contextForObject(parentItem()); 0297 0298 if (!context) { 0299 return; 0300 } 0301 0302 int index = context->contextProperty(QStringLiteral("index")).toInt(); 0303 if (index >= 0) { 0304 triggerGuiEvent(QStringLiteral("page_gained_focus"), QVariantMap({{QStringLiteral("number"), index}, {QStringLiteral("skillId"), m_skillId}})); 0305 } 0306 } 0307 0308 QQuickItem *AbstractDelegate::contentItem() const 0309 { 0310 return m_contentItem; 0311 } 0312 0313 void AbstractDelegate::setContentItem(QQuickItem *item) 0314 { 0315 if (m_contentItem == item) { 0316 return; 0317 } 0318 0319 m_contentItem = item; 0320 item->setParentItem(this); 0321 m_contentItem->setX(m_leftPadding + m_leftInset); 0322 m_contentItem->setY(m_topPadding + m_topInset); 0323 0324 if (m_contentItemAutoWidth && m_contentItemAutoHeight) { 0325 m_contentItem->setSize(QSizeF(width() - m_leftPadding - m_rightPadding - m_leftInset - m_rightInset, 0326 height() - m_topPadding - m_bottomPadding - m_topInset - m_bottomInset)); 0327 } else if (m_contentItemAutoWidth) { 0328 m_contentItem->setWidth(width() - m_leftPadding - m_rightPadding - m_leftInset - m_rightInset); 0329 } else if (m_contentItemAutoHeight) { 0330 m_contentItem->setHeight(height() - m_topPadding - m_bottomPadding- m_topInset - m_bottomInset); 0331 } 0332 0333 emit contentItemChanged(); 0334 } 0335 0336 QQuickItem *AbstractDelegate::background() const 0337 { 0338 return m_backgroundItem; 0339 } 0340 0341 void AbstractDelegate::setBackground(QQuickItem *item) 0342 { 0343 if (m_backgroundItem == item) { 0344 return; 0345 } 0346 0347 m_backgroundItem = item; 0348 m_backgroundItem->setParentItem(this); 0349 m_backgroundItem->setX(0); 0350 m_backgroundItem->setY(0); 0351 m_backgroundItem->setSize(size()); 0352 0353 emit backgroundChanged(); 0354 } 0355 0356 int AbstractDelegate::leftPadding() const 0357 { 0358 return m_leftPadding; 0359 } 0360 0361 void AbstractDelegate::setLeftPadding(int padding) 0362 { 0363 if (m_leftPadding == padding) { 0364 return; 0365 } 0366 0367 m_leftPadding = padding; 0368 syncChildItemsGeometry(size()); 0369 emit leftPaddingChanged(); 0370 emit contentWidthChanged(); 0371 } 0372 0373 0374 int AbstractDelegate::topPadding() const 0375 { 0376 return m_topPadding; 0377 } 0378 0379 void AbstractDelegate::setTopPadding(int padding) 0380 { 0381 if (m_topPadding == padding) { 0382 return; 0383 } 0384 0385 m_topPadding = padding; 0386 syncChildItemsGeometry(size()); 0387 emit topPaddingChanged(); 0388 emit contentHeightChanged(); 0389 } 0390 0391 0392 int AbstractDelegate::rightPadding() const 0393 { 0394 return m_rightPadding; 0395 } 0396 0397 void AbstractDelegate::setRightPadding(int padding) 0398 { 0399 if (m_rightPadding == padding) { 0400 return; 0401 } 0402 0403 m_rightPadding = padding; 0404 syncChildItemsGeometry(size()); 0405 emit rightPaddingChanged(); 0406 emit contentWidthChanged(); 0407 } 0408 0409 0410 int AbstractDelegate::bottomPadding() const 0411 { 0412 return m_bottomPadding; 0413 } 0414 0415 void AbstractDelegate::setBottomPadding(int padding) 0416 { 0417 if (m_bottomPadding == padding) { 0418 return; 0419 } 0420 0421 m_bottomPadding = padding; 0422 syncChildItemsGeometry(size()); 0423 emit bottomPaddingChanged(); 0424 emit contentHeightChanged(); 0425 } 0426 0427 0428 int AbstractDelegate::leftInset() const 0429 { 0430 return m_leftInset; 0431 } 0432 0433 void AbstractDelegate::setLeftInset(int inset) 0434 { 0435 if (m_leftInset == inset) { 0436 return; 0437 } 0438 0439 m_leftInset = inset; 0440 syncChildItemsGeometry(size()); 0441 emit leftInsetChanged(); 0442 emit contentWidthChanged(); 0443 } 0444 0445 0446 int AbstractDelegate::topInset() const 0447 { 0448 return m_topInset; 0449 } 0450 0451 void AbstractDelegate::setTopInset(int inset) 0452 { 0453 if (m_topInset == inset) { 0454 return; 0455 } 0456 0457 m_topInset = inset; 0458 syncChildItemsGeometry(size()); 0459 emit topInsetChanged(); 0460 emit contentHeightChanged(); 0461 } 0462 0463 0464 int AbstractDelegate::rightInset() const 0465 { 0466 return m_rightInset; 0467 } 0468 0469 void AbstractDelegate::setRightInset(int inset) 0470 { 0471 if (m_rightInset == inset) { 0472 return; 0473 } 0474 0475 m_rightInset = inset; 0476 syncChildItemsGeometry(size()); 0477 emit rightInsetChanged(); 0478 emit contentWidthChanged(); 0479 } 0480 0481 0482 int AbstractDelegate::bottomInset() const 0483 { 0484 return m_bottomInset; 0485 } 0486 0487 void AbstractDelegate::setBottomInset(int inset) 0488 { 0489 if (m_bottomInset == inset) { 0490 return; 0491 } 0492 0493 m_bottomInset = inset; 0494 syncChildItemsGeometry(size()); 0495 emit bottomInsetChanged(); 0496 emit contentHeightChanged(); 0497 } 0498 0499 0500 int AbstractDelegate::contentWidth() const 0501 { 0502 return width() - m_leftPadding - m_rightPadding - m_leftInset - m_rightInset; 0503 } 0504 0505 int AbstractDelegate::contentHeight() const 0506 { 0507 return height() - m_topPadding - m_bottomPadding - m_topInset - m_bottomInset; 0508 } 0509 0510 0511 0512 void AbstractDelegate::setSkillView(AbstractSkillView *view) 0513 { 0514 //possible to call only once, by the skillview, setting itself upon instantiation 0515 Q_ASSERT(!m_skillView); 0516 m_skillView = view; 0517 } 0518 0519 AbstractSkillView *AbstractDelegate::skillView() const 0520 { 0521 return m_skillView; 0522 } 0523 0524 void AbstractDelegate::setSessionData(SessionDataMap *data) 0525 { 0526 //possible to call only once, by the skillview upon instantiation 0527 Q_ASSERT(!m_data); 0528 m_data = data; 0529 } 0530 0531 SessionDataMap *AbstractDelegate::sessionData() const 0532 { 0533 return m_data; 0534 } 0535 0536 void AbstractDelegate::setQmlUrl(const QUrl &url) 0537 { 0538 //possible to call only once, by the skillview upon instantiation 0539 Q_ASSERT(m_qmlUrl.isEmpty()); 0540 m_qmlUrl = url; 0541 } 0542 0543 QUrl AbstractDelegate::qmlUrl() const 0544 { 0545 return m_qmlUrl; 0546 } 0547 0548 void AbstractDelegate::setSkillId(const QString &skillId) 0549 { 0550 //possible to call only once, by the skillview upon instantiation 0551 Q_ASSERT(m_skillId.isEmpty()); 0552 m_skillId = skillId; 0553 } 0554 0555 QString AbstractDelegate::skillId() const 0556 { 0557 return m_skillId; 0558 } 0559 0560 #include "moc_abstractdelegate.cpp"