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.
206 lines
6.1 KiB
206 lines
6.1 KiB
#include "binding.h"
|
|
#include "binding_p.h"
|
|
#include "qtmvvm_logging_p.h"
|
|
using namespace QtMvvm;
|
|
|
|
Binding QtMvvm::bind(QObject *viewModel, const char *viewModelProperty, QObject *view, const char *viewProperty, Binding::BindingDirection type, const char *viewModelChangeSignal, const char *viewChangeSignal)
|
|
{
|
|
if(!viewModel) {
|
|
logWarning() << "ViewModel must not be null";
|
|
return {};
|
|
}
|
|
auto moModel = viewModel->metaObject();
|
|
auto piModel = moModel->indexOfProperty(viewModelProperty);
|
|
if(piModel == -1) {
|
|
logWarning() << "ViewModel of type" << moModel->className()
|
|
<< "has no property named" << viewModelProperty;
|
|
return {};
|
|
}
|
|
|
|
if(!view) {
|
|
logWarning() << "View must not be null";
|
|
return {};
|
|
}
|
|
auto moView = view->metaObject();
|
|
auto piView = moView->indexOfProperty(viewProperty);
|
|
if(piView == -1) {
|
|
logWarning() << "View of type" << moView->className()
|
|
<< "has no property named" << viewProperty;
|
|
return {};
|
|
}
|
|
|
|
return bind(viewModel, moModel->property(piModel), view, moView->property(piView), type, viewModelChangeSignal, viewChangeSignal);
|
|
}
|
|
|
|
Binding QtMvvm::bind(QObject *viewModel, const QMetaProperty &viewModelProperty, QObject *view, const QMetaProperty &viewProperty, Binding::BindingDirection type, const char *viewModelChangeSignal, const char *viewChangeSignal)
|
|
{
|
|
if(!viewModel) {
|
|
qWarning() << "ViewModel must not be null";
|
|
return {};
|
|
}
|
|
QMetaMethod smModel;
|
|
if(viewModelChangeSignal) {
|
|
auto moModel = viewModel->metaObject();
|
|
auto siModel = moModel->indexOfSignal(viewModelChangeSignal);
|
|
if(siModel == -1) {
|
|
logWarning() << "ViewModel of type" << moModel->className()
|
|
<< "has no signal named" << viewModelChangeSignal;
|
|
return {};
|
|
}
|
|
smModel = moModel->method(siModel);
|
|
}
|
|
|
|
if(!view) {
|
|
qWarning() << "View must not be null";
|
|
return {};
|
|
}
|
|
QMetaMethod smView;
|
|
if(viewChangeSignal) {
|
|
auto moView = view->metaObject();
|
|
auto siView = moView->indexOfSignal(viewChangeSignal);
|
|
if(siView == -1) {
|
|
logWarning() << "View of type" << moView->className()
|
|
<< "has no signal named" << viewChangeSignal;
|
|
return {};
|
|
}
|
|
smView = moView->method(siView);
|
|
}
|
|
|
|
return BindingPrivate::bind(viewModel, viewModelProperty, view, viewProperty, type, smModel, smView);
|
|
}
|
|
|
|
Binding QtMvvm::bind(QObject *viewModel, const QMetaProperty &viewModelProperty, QObject *view, const QMetaProperty &viewProperty, Binding::BindingDirection type, const QMetaMethod &viewModelChangeSignal, const QMetaMethod &viewChangeSignal)
|
|
{
|
|
//check not null
|
|
if(!viewModel) {
|
|
qWarning() << "ViewModel must not be null";
|
|
return {};
|
|
}
|
|
if(!view) {
|
|
qWarning() << "View must not be null";
|
|
return {};
|
|
}
|
|
|
|
return BindingPrivate::bind(viewModel, viewModelProperty, view, viewProperty, type, viewModelChangeSignal, viewChangeSignal);
|
|
}
|
|
|
|
|
|
Binding::Binding() :
|
|
d(nullptr)
|
|
{}
|
|
|
|
Binding::Binding(QPointer<BindingPrivate> d_ptr) :
|
|
d(std::move(d_ptr))
|
|
{}
|
|
|
|
Binding::~Binding() = default;
|
|
|
|
bool Binding::isValid() const
|
|
{
|
|
return d;
|
|
}
|
|
|
|
void Binding::unbind()
|
|
{
|
|
if(d) {
|
|
d->deleteLater();
|
|
d.clear();
|
|
}
|
|
}
|
|
|
|
// ------------- Private Implementation -------------
|
|
|
|
Binding BindingPrivate::bind(QObject *viewModel, QMetaProperty viewModelProperty, QObject *view, QMetaProperty viewProperty, Binding::BindingDirection type, QMetaMethod viewModelChangeSignal, QMetaMethod viewChangeSignal)
|
|
{
|
|
QPointer<BindingPrivate> binderPrivate = new BindingPrivate(viewModel, std::move(viewModelProperty), view, std::move(viewProperty));
|
|
|
|
if(type.testFlag(Binding::SingleInit))
|
|
binderPrivate->init();
|
|
if(type.testFlag(Binding::OneWayToView))
|
|
binderPrivate->bindFrom(std::move(viewModelChangeSignal));
|
|
if(type.testFlag(Binding::OneWayToViewModel))
|
|
binderPrivate->bindTo(std::move(viewChangeSignal));
|
|
|
|
return binderPrivate;
|
|
}
|
|
|
|
BindingPrivate::BindingPrivate(QObject *viewModel, QMetaProperty viewModelProperty, QObject *view, QMetaProperty viewProperty) :
|
|
QObject(view),
|
|
viewModel(viewModel),
|
|
view(view),
|
|
viewModelProperty(std::move(viewModelProperty)),
|
|
viewProperty(std::move(viewProperty))
|
|
{
|
|
connect(viewModel, &QObject::destroyed,
|
|
this, &BindingPrivate::deleteLater);
|
|
}
|
|
|
|
void BindingPrivate::viewModelTrigger()
|
|
{
|
|
viewProperty.write(view, viewModelProperty.read(viewModel));
|
|
}
|
|
|
|
void BindingPrivate::viewTrigger()
|
|
{
|
|
viewModelProperty.write(viewModel, viewProperty.read(view));
|
|
}
|
|
|
|
void BindingPrivate::init()
|
|
{
|
|
testReadable(viewModelProperty, false);
|
|
testWritable(viewProperty, true);
|
|
viewProperty.write(view, viewModelProperty.read(viewModel));
|
|
}
|
|
|
|
void BindingPrivate::bindFrom(QMetaMethod changeSignal)
|
|
{
|
|
//testReadable and testWritable are alredy tested by the init() method
|
|
if(!changeSignal.isValid()) {
|
|
testNotifier(viewModelProperty, false);
|
|
changeSignal = viewModelProperty.notifySignal();
|
|
}
|
|
auto trigger = metaObject()->method(metaObject()->indexOfSlot("viewModelTrigger()"));
|
|
connect(viewModel, changeSignal, this, trigger);
|
|
}
|
|
|
|
void BindingPrivate::bindTo(QMetaMethod changeSignal)
|
|
{
|
|
testReadable(viewProperty, true);
|
|
testWritable(viewModelProperty, false);
|
|
if(!changeSignal.isValid()) {
|
|
testNotifier(viewProperty, true);
|
|
changeSignal = viewProperty.notifySignal();
|
|
}
|
|
auto trigger = metaObject()->method(metaObject()->indexOfSlot("viewTrigger()"));
|
|
connect(view, changeSignal, this, trigger);
|
|
}
|
|
|
|
void BindingPrivate::testReadable(const QMetaProperty &property, bool asView) const
|
|
{
|
|
if(!property.isReadable()) {
|
|
logWarning() << (asView ? "View" : "ViewModel")
|
|
<< "property" << property.name()
|
|
<< "of" << property.enclosingMetaObject()->className()
|
|
<< "is not readable";
|
|
}
|
|
}
|
|
|
|
void BindingPrivate::testWritable(const QMetaProperty &property, bool asView) const
|
|
{
|
|
if(!property.isWritable()) {
|
|
logWarning() << (asView ? "View" : "ViewModel")
|
|
<< "property" << property.name()
|
|
<< "of" << property.enclosingMetaObject()->className()
|
|
<< "is not writable";
|
|
}
|
|
}
|
|
|
|
void BindingPrivate::testNotifier(const QMetaProperty &property, bool asView) const
|
|
{
|
|
if(!property.hasNotifySignal()) {
|
|
logWarning() << (asView ? "View" : "ViewModel")
|
|
<< "property" << property.name()
|
|
<< "of" << property.enclosingMetaObject()->className()
|
|
<< "has no notify singal";
|
|
}
|
|
}
|
|
|