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 &parameters)
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"