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.
		
		
		
		
		
			
		
			
				
					
					
						
							201 lines
						
					
					
						
							5.0 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							201 lines
						
					
					
						
							5.0 KiB
						
					
					
				| #include "qqmlviewplaceholder.h" | |
| #include <QtCore/QMetaMethod> | |
| #include <QtQml/QQmlInfo> | |
| #include <QtQml/QQmlProperty> | |
| #include <QtMvvmCore/CoreApp> | |
| using namespace QtMvvm; | |
| 
 | |
| QQmlViewPlaceholder::QQmlViewPlaceholder(QQuickItem *parent) : | |
| 	QQuickItem{parent} | |
| { | |
| 	// auto presenting | |
| 	connect(this, &QQmlViewPlaceholder::viewModelTypeChanged, | |
| 			this, &QQmlViewPlaceholder::presentIfReady); | |
| 	connect(this, &QQmlViewPlaceholder::autoPresentChanged, | |
| 			this, &QQmlViewPlaceholder::presentIfReady); | |
| 
 | |
| 	// size changes | |
| 	connect(this, &QQmlViewPlaceholder::widthChanged, | |
| 			this, &QQmlViewPlaceholder::resizeView); | |
| 	connect(this, &QQmlViewPlaceholder::heightChanged, | |
| 			this, &QQmlViewPlaceholder::resizeView); | |
| 	connect(this, &QQmlViewPlaceholder::autoResizeViewChanged, | |
| 			this, &QQmlViewPlaceholder::resizeView); | |
| } | |
| 
 | |
| bool QQmlViewPlaceholder::presentItem(const QVariant &item) | |
| { | |
| 	// check if the parameter is valid | |
| 	auto quickItem = item.value<QQuickItem*>(); | |
| 	if(!quickItem) { | |
| 		qmlWarning(this) << "presentItem called with invalid item of type " << item.typeName(); | |
| 		return false; | |
| 	} | |
| 
 | |
| 	// handle already existing view case | |
| 	if(_loadedView) { | |
| 		if(_replaceViews) { // quick discard without reenableing all children | |
| 			_loadedView->setVisible(false); | |
| 			_loadedView->deleteLater(); | |
| 			_loadedView = nullptr; | |
| 		} else { | |
| 			qmlWarning(this) << R"(A view has already been presented. Discard it first via "discardView" or set "replaceViews" to true)"; | |
| 			return false; | |
| 		} | |
| 	} | |
| 
 | |
| 	// add | |
| 	_loadedView = quickItem; | |
| 	quickItem->setParent(this); | |
| 	quickItem->setParentItem(this); | |
| 	resizeView(); | |
| 	quickItem->setVisible(true); | |
| 
 | |
| 	// hide all children | |
| 	for(auto child : childItems()) { | |
| 		if(child != _loadedView) | |
| 			child->setVisible(false); | |
| 	} | |
| 
 | |
| 	emit loadedViewChanged(_loadedView); | |
| 	return true; | |
| } | |
| 
 | |
| bool QQmlViewPlaceholder::closeAction() | |
| { | |
| 	if(!_loadedView) | |
| 		return false; | |
| 
 | |
| 	// call close on the view | |
| 	auto vMetaObject = _loadedView->metaObject(); | |
| 	auto cMethodIndex = vMetaObject->indexOfMethod("closeAction()"); | |
| 	if(cMethodIndex != -1) { | |
| 		auto cMethod = vMetaObject->method(cMethodIndex); | |
| 		QVariant ok = false; | |
| 		cMethod.invoke(_loadedView, Q_RETURN_ARG(QVariant, ok)); | |
| 		if(ok.toBool()) | |
| 			return true; | |
| 	} | |
| 
 | |
| 	// close the view itself (if wished) | |
| 	if(_closeViewOnAction) { | |
| 		discardView(); | |
| 		return true; | |
| 	} | |
| 
 | |
| 	// otherwise: nothing closed | |
| 	return false; | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::setParentViewModel(ViewModel *parentViewModel) | |
| { | |
| 	// first: clear the auto-connected viewmodel, if required | |
| 	if(_clearParentVmCon && _parentVmCon) | |
| 		disconnect(_parentVmCon); | |
| 
 | |
| 	// then: set property as usual | |
| 	if(_parentViewModel == parentViewModel) | |
| 		return; | |
| 
 | |
| 	_parentViewModel = parentViewModel; | |
| 	emit parentViewModelChanged(_parentViewModel); | |
| 
 | |
| 	// check the vm parent for a presenter method | |
| 	auto view = qobject_cast<QQuickItem*>(_parentViewModel->parent()); | |
| 	if(view) { | |
| 		if(view->metaObject()->indexOfMethod("presentItem(QVariant)") == -1) | |
| 			qmlWarning(this) << R"(Parent item of "parentViewModel" does not have a "presentItem" method. Check the ViewPlaceholder documentation!)"; | |
| 	} else | |
| 		qmlWarning(this) << R"(Parent item of "parentViewModel" is not an Item)"; | |
| 
 | |
| 	presentIfReady(); | |
| } | |
| 
 | |
| QQuickItem *QQmlViewPlaceholder::loadedView() const | |
| { | |
| 	return _loadedView; | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::componentComplete() | |
| { | |
| 	// auto-set the vm if not already set | |
| 	if(!_parentViewModel) | |
| 		getParentViewModel(); | |
| 
 | |
| 	// last step: call base implementation, then present | |
| 	QQuickItem::componentComplete(); | |
| 	_isReady = true; | |
| 	presentIfReady(); | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::presentView() | |
| { | |
| 	CoreApp::show(qUtf8Printable(_viewModelType), _showParams, _parentViewModel); | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::discardView() | |
| { | |
| 	// hide view | |
| 	_loadedView->setVisible(false); | |
| 
 | |
| 	// show all children again | |
| 	for(auto child : childItems()) { | |
| 		if(child != _loadedView) | |
| 			child->setVisible(true); | |
| 	} | |
| 
 | |
| 	// now delete it | |
| 	_loadedView->deleteLater(); | |
| 	_loadedView = nullptr; | |
| 	emit loadedViewChanged(nullptr); | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::parentItemVmChanged(ViewModel *viewModel) | |
| { | |
| 	// set vm without clearing the connection | |
| 	_clearParentVmCon = false; | |
| 	setParentViewModel(viewModel); | |
| 	_clearParentVmCon = true; | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::presentIfReady() | |
| { | |
| 	if(_isReady && | |
| 	   _autoPresent && | |
| 	   !_loadedView && | |
| 	   _parentViewModel && | |
| 	   !_viewModelType.isEmpty()) | |
| 		presentView(); | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::resizeView() | |
| { | |
| 	if(_loadedView && _autoResizeView) { | |
| 		_loadedView->setWidth(width()); | |
| 		_loadedView->setHeight(height()); | |
| 	} | |
| } | |
| 
 | |
| void QQmlViewPlaceholder::getParentViewModel() | |
| { | |
| 	auto pItem = parentItem(); | |
| 	if(!pItem) | |
| 		return; | |
| 
 | |
| 	QQmlProperty vmProp{pItem, QStringLiteral("viewModel")}; | |
| 	if(!vmProp.isValid()) | |
| 		return; | |
| 
 | |
| 	// set the vm from the property | |
| 	auto vm = vmProp.read().value<ViewModel*>(); | |
| 	if(vm) | |
| 		setParentViewModel(vm); // no warning here - might be ok for lazy presented vms | |
|  | |
| 	// connect to further changes, via helper slot | |
| 	auto cSlotIndex = metaObject()->indexOfSlot("parentItemVmChanged(QtMvvm::ViewModel*)"); | |
| 	Q_ASSERT(cSlotIndex != -1); | |
| 	if(_parentVmCon) | |
| 		disconnect(_parentVmCon); | |
| 	_parentVmCon = connect(pItem, vmProp.property().notifySignal(), | |
| 						   this, metaObject()->method(cSlotIndex)); | |
| }
 | |
| 
 |