diff --git a/playground/models/CMakeLists.txt b/playground/models/CMakeLists.txt index af17bb84..541f5992 100644 --- a/playground/models/CMakeLists.txt +++ b/playground/models/CMakeLists.txt @@ -3,6 +3,6 @@ # SPDX-License-Identifier: BSD-3-Clause ############################################################################ -qsk_add_example(models Window.h Window.cpp QskDataControlMapper.cpp QskDataControlMapper.h main.cpp) +qsk_add_example(models QskModelObjectBinder.cpp QskModelObjectBinder.h Window.h Window.cpp main.cpp) target_link_libraries(models PRIVATE Qt::Sql) \ No newline at end of file diff --git a/playground/models/QskDataControlMapper.cpp b/playground/models/QskDataControlMapper.cpp deleted file mode 100644 index cc3fff1e..00000000 --- a/playground/models/QskDataControlMapper.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by aldo on 27/01/21. -// - -#include "QskDataControlMapper.h" - -QskDataControlMapper::QskDataControlMapper() : m(nullptr){ - -} - -void QskDataControlMapper::addMapping(QskControl *widget, int section) { - if(widget) - addMapping(widget,section,widget->metaObject()->userProperty().name()); -} - -void QskDataControlMapper::addMapping(QskControl *widget, int section, const QByteArray &propertyName) { - Item item; - item.control=widget; - item.section=section; - item.property=propertyName; - item.index=m->index(currentIndex.row(),section); - items.append(item); -} - -void QskDataControlMapper::setModel(QAbstractItemModel *model) { - m=model; - currentIndex=QModelIndex(); - if(m) { - connect(m,&QAbstractItemModel::dataChanged,this, - &QskDataControlMapper::_dataChanged); - } -} - -bool QskDataControlMapper::qContainsIndex(const QModelIndex & idx, const QModelIndex & topLeft, const QModelIndex & bottomRight) -{ - return idx.row() >= topLeft.row() && idx.row() <= bottomRight.row() - && idx.column() >= topLeft.column() && idx.column() <= bottomRight.column(); -} - -void QskDataControlMapper::_dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector &) -{ - for (auto& x : items) { - if (qContainsIndex(x.index, topLeft, bottomRight)) { - populate(x); - } - } -} - -void QskDataControlMapper::populate(QskDataControlMapper::Item & x) -{ - x.control->setProperty(x.property, - m->index(currentIndex.row(),x.section).data(Qt::EditRole)); -} - -void QskDataControlMapper::populate() -{ - for (auto& x : items) { - populate(x); - } -} - -void QskDataControlMapper::submit() { - if(!m) - return; - for(auto & x : items) { - m->setData(m->index(currentIndex.row(), x.section), - x.control->property(x.property)); - } -} - -void QskDataControlMapper::setCurrentIndex(int index) { - if(!m) - return; - currentIndex=m->index(index,0); - populate(); -} - -#include "moc_QskDataControlMapper.cpp" diff --git a/playground/models/QskDataControlMapper.h b/playground/models/QskDataControlMapper.h deleted file mode 100644 index 3d20dab5..00000000 --- a/playground/models/QskDataControlMapper.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef QSKDATACONTROLMAPPER_H -#define QSKDATACONTROLMAPPER_H - -#include -#include - -class QskDataControlMapper : public QObject { - Q_OBJECT - -private: - - struct Item; - -public: - - QskDataControlMapper(); - - void addMapping(QskControl* widget, int section); - void addMapping(QskControl* widget, int section, const QByteArray &propertyName); - - void setModel(QAbstractItemModel* model); - - static bool qContainsIndex(const QModelIndex &idx, const QModelIndex &topLeft, - const QModelIndex &bottomRight); - - void _dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &); - - void populate(QskDataControlMapper::Item & x); - - void populate (); - - void submit(); - - void setCurrentIndex(int index); - -private: - - QList items; - - QAbstractItemModel* m; - QPersistentModelIndex currentIndex; - - struct Item { - QPointer control; - int section; - QByteArray property; - QPersistentModelIndex index; - }; - -}; - - - -#endif //QSKDATACONTROLMAPPER_H \ No newline at end of file diff --git a/playground/models/QskModelObjectBinder.cpp b/playground/models/QskModelObjectBinder.cpp new file mode 100644 index 00000000..51371caf --- /dev/null +++ b/playground/models/QskModelObjectBinder.cpp @@ -0,0 +1,160 @@ +// +// Created by aldo on 27/01/21. +// + +#include "QskModelObjectBinder.h" + +#include +#include +#include + +class QskModelObjectBinder::PrivateData +{ +public: + PrivateData( ) + { + } + + struct Item { + Item() :valid(false), column(-1) {}; + Item(QObject *obj, int column, const QByteArray& property) + : valid (true), object( obj ), column( column ), property( property ) + { + }; + bool valid; + QPointer< QObject > object; + int column; + QByteArray property; + }; + void updateObjectProperty(Item & x) + { + Q_ASSERT( x.object != nullptr ); + Q_ASSERT( ! x.property.isEmpty() ); + x.object->setProperty(x.property, + m->index(modelIndex.row(),x.column).data(Qt::EditRole)); + } + + void updateAllObjects() + { + for (auto it = items.begin(); it != items.end(); ++it) { + updateObjectProperty(it.value()); + } + } + + void updateModelField(Item& x) + { + Q_ASSERT( x.valid ); + Q_ASSERT( x.object != nullptr ); + auto prop =x.object->property( x.property ); + Q_ASSERT( prop.isValid() ); + + m->setData(m->index(modelIndex.row(),x.column),prop); + } + + void updateModelFields() + { + for (auto it = items.begin(); it != items.end(); ++it) + { + updateModelField( it.value() ); + } + } + +public: + QMap items; + QAbstractItemModel* m; + QPersistentModelIndex modelIndex; +}; + +static bool modelContainsIndex(const QModelIndex & idx, const QModelIndex & topLeft, const QModelIndex & bottomRight) +{ + return idx.row() >= topLeft.row() && idx.row() <= bottomRight.row() + && idx.column() >= topLeft.column() && idx.column() <= bottomRight.column(); +} + +QskModelObjectBinder::QskModelObjectBinder(QObject* parent) +: QObject( parent ) +{ + m_data=std::make_unique( ); +} + +QskModelObjectBinder::QskModelObjectBinder(QAbstractItemModel *model, QObject* parent) +: QObject( parent ) +{ + m_data=std::make_unique(); + bindModel(model); +} + + +void QskModelObjectBinder::bindObject(QObject *obj, int column, const QByteArray &propertyName) +{ + Q_ASSERT( obj != nullptr ); + + if(obj == nullptr) + return; + + QMetaProperty prop; + auto meta = obj->metaObject(); + if (propertyName.isEmpty()) + { + prop = meta->userProperty(); + }else + { + auto idx = meta->indexOfProperty( propertyName ); + if(idx>=0) + { + prop=meta->property(idx); + } + } + Q_ASSERT(! prop.isValid()); + + PrivateData::Item item(obj,column,prop.name()); + m_data->items[obj]=item; +} + +void QskModelObjectBinder::unbindObject(QObject *obj) +{ + m_data->items.remove(obj); +} + +void QskModelObjectBinder::bindModel(QAbstractItemModel *model) +{ + m_data->m=model; + m_data->modelIndex=QModelIndex(); + if(model) { + connect(m_data->m,&QAbstractItemModel::dataChanged,this, + &QskModelObjectBinder::modelDataChanged); + } +} + +void QskModelObjectBinder::modelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector &) +{ + const int row = m_data->modelIndex.row(); + for (auto it = m_data->items.begin(); it != m_data->items.end(); ++it) { + if (modelContainsIndex(m_data->m->index(row,it.value().column), topLeft, bottomRight)) { + m_data->updateObjectProperty(it.value()); + } + } +} + +void QskModelObjectBinder::selectRow( int row ) +{ + Q_ASSERT( m_data->m != nullptr ); + + if ( !m_data->m ) + return; + + m_data->modelIndex = m_data->m->index( row, 0 ); + m_data->updateAllObjects(); +} + +int QskModelObjectBinder::currentRow() const +{ + return m_data->modelIndex.row(); +} + +void QskModelObjectBinder::updateModel() +{ + m_data->updateModelFields(); +} + +#include "moc_QskModelObjectBinder.cpp" diff --git a/playground/models/QskModelObjectBinder.h b/playground/models/QskModelObjectBinder.h new file mode 100644 index 00000000..88a7db54 --- /dev/null +++ b/playground/models/QskModelObjectBinder.h @@ -0,0 +1,34 @@ +#ifndef QSKDATACONTROLMAPPER_H +#define QSKDATACONTROLMAPPER_H + +#include +#include + +class QskModelObjectBinder : public QObject { + Q_OBJECT + +public: + QskModelObjectBinder(QObject *parent=nullptr); + QskModelObjectBinder(QAbstractItemModel* model, QObject *parent=nullptr); + + void bindObject(QObject*, int column, const QByteArray &propertyName=""); + void unbindObject(QObject*o); + + void bindModel(QAbstractItemModel* model); + + void selectRow(int row); + [[nodiscard]] int currentRow() const; + + void updateModel(); + +private Q_SLOTS: + void modelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector &); + +private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + + + +#endif //QSKDATACONTROLMAPPER_H \ No newline at end of file diff --git a/playground/models/Window.cpp b/playground/models/Window.cpp index 2fa19a15..ac9e25d5 100644 --- a/playground/models/Window.cpp +++ b/playground/models/Window.cpp @@ -12,11 +12,13 @@ #include #include -#include +#include -#include #include #include +#include +#include +#include Window::Window( ) @@ -36,18 +38,18 @@ Window::Window( ) auto txt = new QskTextInput(); auto spin = new QskSpinBox(); - auto mapper = new QskDataControlMapper(); - mapper->setModel(table); + auto mapper = new QskModelObjectBinder(); + mapper->bindModel(table); /* Not needed if we set USER true for the corresponding Q_PROPERTY in QskBoundedInput and QskTextInput maybe could be done also for other controls - mapper->addMapping(spin, 0,"value"); - mapper->addMapping(txt, 1, "text"); + mapper->bindControl(spin, 0,"value"); + mapper->bindControl(txt, 1, "text"); */ - mapper->addMapping(spin, 0); // needs USER=true for value in QskBoundedInput - mapper->addMapping(txt, 1); // needs USER=true for text in QskTextInput + mapper->bindObject(spin, 0); // needs USER=true for value in QskBoundedInput + mapper->bindObject(txt, 1); // needs USER=true for text in QskTextInput - mapper->setCurrentIndex(0); + mapper->selectRow(0); auto v = new QskLinearBox(Qt::Vertical); v->addSpacer( 0,100 ); @@ -57,9 +59,16 @@ Window::Window( ) auto h = new QskLinearBox(Qt::Horizontal); auto prev = new QskPushButton("<",h); auto next = new QskPushButton(">",h); - connect(prev,&QskPushButton::clicked,[=]() { mapper->setCurrentIndex( 0 ); }); - connect(next,&QskPushButton::clicked,[=]() { mapper->setCurrentIndex( 1 ); }); + connect(prev,&QskPushButton::clicked,[=]() { mapper->selectRow( 0 ); }); + connect(next,&QskPushButton::clicked,[=]() { mapper->selectRow( 1 ); }); v->addItem(h); + + auto save = new QskPushButton("Save",v); + connect(save,&QskPushButton::clicked,[=]() { + mapper->updateModel(); + auto r = table->record(mapper->currentRow()); + qDebug() << r; + }); v->addSpacer( 0,100 ); }