You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
6.9 KiB
238 lines
6.9 KiB
#include "qqmlquickpresenter.h"
|
|
|
|
#include <QtQml/QtQml>
|
|
|
|
#include <QtQuick/QQuickItem>
|
|
|
|
#include <QtQuickControls2/QQuickStyle>
|
|
|
|
#include <QtMvvmCore/private/qtmvvm_logging_p.h>
|
|
#include <QtMvvmQuick/private/quickpresenter_p.h>
|
|
|
|
#ifdef Q_OS_ANDROID
|
|
#include <QtAndroidExtras/QtAndroid>
|
|
#endif
|
|
|
|
using namespace QtMvvm;
|
|
|
|
QQmlQuickPresenter::QQmlQuickPresenter(QQmlEngine *engine) :
|
|
QObject{engine},
|
|
_engine{engine}
|
|
{
|
|
QuickPresenterPrivate::setQmlPresenter(this);
|
|
connect(QuickPresenterPrivate::currentPresenter(), &QuickPresenter::inputViewFactoryChanged,
|
|
this, &QQmlQuickPresenter::inputViewFactoryChanged);
|
|
}
|
|
|
|
QString QQmlQuickPresenter::currentStyle() const
|
|
{
|
|
return QQuickStyle::name();
|
|
}
|
|
|
|
InputViewFactory *QQmlQuickPresenter::inputViewFactory() const
|
|
{
|
|
return QuickPresenterPrivate::currentPresenter()->inputViewFactory();
|
|
}
|
|
|
|
bool QQmlQuickPresenter::isViewLoading() const
|
|
{
|
|
return _latestComponent;
|
|
}
|
|
|
|
qreal QQmlQuickPresenter::loadingProgress() const
|
|
{
|
|
return _latestComponent ? _latestComponent->progress() : -1.0;
|
|
}
|
|
|
|
QStringList QQmlQuickPresenter::mimeTypeFilters(const QStringList &mimeTypes) const
|
|
{
|
|
QMimeDatabase db;
|
|
QStringList filters;
|
|
filters.reserve(mimeTypes.size());
|
|
for(const auto &mime : mimeTypes) {
|
|
auto mType = db.mimeTypeForName(mime);
|
|
if(mType.isValid())
|
|
filters.append(mType.filterString());
|
|
}
|
|
return filters;
|
|
}
|
|
|
|
void QQmlQuickPresenter::toggleDrawer()
|
|
{
|
|
if(!_qmlPresenter) {
|
|
logWarning() << "No QML-Presenter registered! Unable to toggle drawer";
|
|
return;
|
|
}
|
|
|
|
if(!QMetaObject::invokeMethod(_qmlPresenter, "toggleDrawer"))
|
|
logWarning() << "QML-Presenter does not have a \"toggleDrawer\" method";
|
|
}
|
|
|
|
void QQmlQuickPresenter::popView()
|
|
{
|
|
if(!_qmlPresenter) {
|
|
logWarning() << "No QML-Presenter registered! Unable to pop view";
|
|
return;
|
|
}
|
|
|
|
if(!QMetaObject::invokeMethod(_qmlPresenter, "closeAction"))
|
|
logWarning() << "QML-Presenter does not have a \"closeAction\" method";
|
|
}
|
|
|
|
void QQmlQuickPresenter::hapticLongPress()
|
|
{
|
|
#ifdef Q_OS_ANDROID
|
|
QtAndroid::runOnAndroidThread([](){
|
|
const auto content = QAndroidJniObject::getStaticField<jint>("android/R$id", "content");
|
|
const auto LONG_PRESS = QAndroidJniObject::getStaticField<jint>("android/view/HapticFeedbackConstants", "LONG_PRESS");
|
|
|
|
auto activity = QtAndroid::androidActivity();
|
|
auto view = activity.callObjectMethod("findViewById",
|
|
"(I)Landroid/view/View;",
|
|
content);
|
|
view.callMethod<jboolean>("performHapticFeedback",
|
|
"(I)Z",
|
|
LONG_PRESS);
|
|
});
|
|
#else
|
|
logDebug() << Q_FUNC_INFO;
|
|
#endif
|
|
}
|
|
|
|
void QQmlQuickPresenter::present(ViewModel *viewModel, const QVariantHash ¶ms, const QUrl &viewUrl, QPointer<ViewModel> parent)
|
|
{
|
|
auto component = _componentCache.object(viewUrl);
|
|
if(component) {
|
|
_loadQueue.enqueue(std::make_tuple(*component, viewModel, params, parent));
|
|
processShowQueue();
|
|
} else {
|
|
//create component (and replace latest)
|
|
if(_latestComponent) {
|
|
disconnect(_latestComponent, &QQmlComponent::progressChanged,
|
|
this, &QQmlQuickPresenter::loadingProgressChanged);
|
|
}
|
|
_latestComponent = new QQmlComponent{_engine};
|
|
component = new QSharedPointer<QQmlComponent>{_latestComponent.data()};
|
|
_componentCache.insert(viewUrl, component);
|
|
_loadQueue.enqueue(std::make_tuple(*component, viewModel, params, parent));
|
|
|
|
//setup ui status
|
|
emit viewLoadingChanged(true);
|
|
emit loadingProgressChanged(0.0);
|
|
connect(_latestComponent, &QQmlComponent::progressChanged,
|
|
this, &QQmlQuickPresenter::loadingProgressChanged);
|
|
connect(_latestComponent, &QQmlComponent::statusChanged,
|
|
this, &QQmlQuickPresenter::statusChanged);
|
|
_latestComponent->loadUrl(viewUrl, QQmlComponent::PreferSynchronous);
|
|
}
|
|
}
|
|
|
|
void QQmlQuickPresenter::showDialog(const MessageConfig &config, MessageResult *result)
|
|
{
|
|
if(!_qmlPresenter) {
|
|
logWarning() << "No QML-Presenter registered! Unable to present dialog of type"
|
|
<< config.type();
|
|
return;
|
|
}
|
|
|
|
QVariant res = false;
|
|
QMetaObject::invokeMethod(_qmlPresenter, "showDialog", Qt::DirectConnection,
|
|
Q_RETURN_ARG(QVariant, res),
|
|
Q_ARG(QVariant, QVariant::fromValue(config)),
|
|
Q_ARG(QVariant, QVariant::fromValue(result)));
|
|
if(!res.toBool()) {
|
|
logWarning() << "Failed to present dialog of type"
|
|
<< config.type();
|
|
result->complete(MessageConfig::NoButton);
|
|
}
|
|
}
|
|
|
|
void QQmlQuickPresenter::statusChanged(QQmlComponent::Status status)
|
|
{
|
|
auto component = qobject_cast<QQmlComponent*>(sender());
|
|
if(!component)
|
|
return;
|
|
|
|
switch(status) {
|
|
case QQmlComponent::Ready:
|
|
{
|
|
logDebug() << "Loaded and cached component" << component->url();
|
|
break;
|
|
}
|
|
case QQmlComponent::Error:
|
|
{
|
|
logWarning().noquote() << "Failed to load component" << component->url()
|
|
<< "with error:" << component->errorString().trimmed();
|
|
_componentCache.remove(component->url());
|
|
break;
|
|
}
|
|
default:
|
|
return; //not break. code after must not be executed in this case
|
|
}
|
|
|
|
if(_latestComponent == component) {
|
|
disconnect(component, &QQmlComponent::progressChanged,
|
|
this, &QQmlQuickPresenter::loadingProgressChanged);
|
|
_latestComponent = nullptr;
|
|
emit viewLoadingChanged(false);
|
|
}
|
|
processShowQueue();
|
|
}
|
|
|
|
void QQmlQuickPresenter::processShowQueue()
|
|
{
|
|
while(!_loadQueue.isEmpty()) {
|
|
auto loadInfo = _loadQueue.head();
|
|
switch(std::get<0>(loadInfo)->status()){
|
|
case QQmlComponent::Ready:
|
|
addObject(std::get<0>(loadInfo).data(), std::get<1>(loadInfo), std::get<2>(loadInfo), std::get<3>(loadInfo));
|
|
break;
|
|
case QQmlComponent::Null:
|
|
case QQmlComponent::Error:
|
|
std::get<1>(loadInfo)->deleteLater();
|
|
break;
|
|
default:
|
|
return; //not break. code after must not be executed in this case
|
|
}
|
|
_loadQueue.dequeue();
|
|
}
|
|
}
|
|
|
|
void QQmlQuickPresenter::addObject(QQmlComponent *component, ViewModel *viewModel, const QVariantHash ¶ms, const QPointer<ViewModel> &parent)
|
|
{
|
|
if(!_qmlPresenter) {
|
|
logWarning() << "No QML-Presenter registered! Unable to present viewModel of type"
|
|
<< viewModel->metaObject()->className();
|
|
return;
|
|
}
|
|
|
|
//create the view item, set initial stuff and them complete creation
|
|
auto item = component->beginCreate(_engine->rootContext());
|
|
if(!item) {
|
|
logWarning() << "Unable to create quick view from the loaded component"
|
|
<< component->url();
|
|
return;
|
|
}
|
|
item->setProperty("viewModel", QVariant::fromValue(viewModel));
|
|
viewModel->setParent(item);
|
|
viewModel->onInit(params);
|
|
component->completeCreate();
|
|
|
|
auto presented = false;
|
|
auto cPresenter = QuickPresenterPrivate::currentPresenter();
|
|
if(parent && parent->parent())
|
|
presented = cPresenter->presentToQml(parent->parent(), item);
|
|
if(!presented)
|
|
presented = cPresenter->presentToQml(_qmlPresenter, item);
|
|
|
|
if(presented) {
|
|
if(!item->parent())
|
|
QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership);
|
|
logDebug() << "Presented" << viewModel->metaObject()->className()
|
|
<< "with view" << item->metaObject()->className();
|
|
} else {
|
|
logWarning() << "Failed to present item for viewModel of type"
|
|
<< viewModel->metaObject()->className();
|
|
item->deleteLater();
|
|
}
|
|
}
|
|
|