├── .gitignore ├── MVVM ├── be │ └── codeminded │ │ └── mvvm │ │ └── qmldir ├── Commands │ ├── RelayCommand.cpp │ ├── EmitCommand.cpp │ ├── AbstractCommand.h │ ├── EmitCommand.h │ ├── CommandProxy.h │ ├── RelayCommand.h │ ├── AbstractAsyncCommand.h │ ├── ListCommand.h │ ├── CommandProxy.cpp │ ├── CompositeCommand.h │ ├── ListCommand.cpp │ ├── AbstractAsyncCommand.cpp │ └── CompositeCommand.cpp ├── MvvmPlugin.h ├── Models │ ├── QmlQObjectList.h │ ├── CommandListModel.h │ └── CommandListModel.cpp ├── MVVM.pro └── MvvmPlugin.cpp ├── Examples ├── qml.qrc ├── Example.pro ├── asynchellocommand.h ├── viewmodel.cpp ├── examplelistclass.h ├── Example.cpp ├── asynchellocommand.cpp ├── View.qml ├── Example.qml └── viewmodel.h ├── mvvm.pro └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | mvvm.pro.user 2 | -------------------------------------------------------------------------------- /MVVM/be/codeminded/mvvm/qmldir: -------------------------------------------------------------------------------- 1 | module be.codeminded.mvvm 2 | plugin mvvmplugin 3 | classname MvvmPlugin 4 | typeinfo plugins.qmltypes 5 | -------------------------------------------------------------------------------- /Examples/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example.qml 4 | View.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /mvvm.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS = MVVM Examples 4 | 5 | MVVM.file = "MVVM/MVVM.pro" 6 | Examples.file = "Examples/Example.pro" 7 | 8 | Examples.depends = MVVM 9 | -------------------------------------------------------------------------------- /MVVM/Commands/RelayCommand.cpp: -------------------------------------------------------------------------------- 1 | #include "RelayCommand.h" 2 | 3 | bool RelayCommand::canExecute() const 4 | { 5 | return canExecuteDelegate(); 6 | } 7 | 8 | void RelayCommand::evaluateCanExecute() 9 | { 10 | emit canExecuteChanged( canExecute() ); 11 | } 12 | 13 | void RelayCommand::execute() 14 | { 15 | executeDelegate(); 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mvvm 2 | Model View ViewModel techniques with Qt and QtQuick 3 | 4 | Some info available at these urls: 5 | 6 | http://pvanhoof.be/blog/index.php/2017/08/18/abstractcommand-model-view-viewmodel-techniques 7 | http://pvanhoof.be/blog/index.php/2017/08/24/the-relaycommand-in-qt 8 | http://pvanhoof.be/blog/index.php/2017/10/23/asynchronous-commands 9 | -------------------------------------------------------------------------------- /MVVM/MvvmPlugin.h: -------------------------------------------------------------------------------- 1 | #ifndef MVVMPLUGIN_H 2 | #define MVVMPLUGIN_H 3 | 4 | #include 5 | 6 | class MvvmPlugin: public QQmlExtensionPlugin 7 | { 8 | Q_OBJECT 9 | Q_PLUGIN_METADATA( IID "org.qt-project.Qt.QQmlExtensionInterface" ) 10 | public: 11 | MvvmPlugin() {} 12 | virtual ~MvvmPlugin() {} 13 | void registerTypes(const char* a_uri); 14 | }; 15 | 16 | #endif // MVVMPLUGIN_H 17 | -------------------------------------------------------------------------------- /MVVM/Commands/EmitCommand.cpp: -------------------------------------------------------------------------------- 1 | #include "EmitCommand.h" 2 | 3 | void EmitCommand::setCanExecute(bool canExecute) 4 | { 5 | if (canExe != canExecute) { 6 | canExe = canExecute; 7 | emit canExecuteChanged(canExe); 8 | emit privateCanExecuteChanged(); 9 | } 10 | } 11 | 12 | bool EmitCommand::canExecute() const 13 | { 14 | return canExe; 15 | } 16 | 17 | void EmitCommand::execute() 18 | { 19 | emit executes(); 20 | } 21 | -------------------------------------------------------------------------------- /MVVM/Commands/AbstractCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACT_COMMAND_H 2 | #define ABSTRACT_COMMAND_H 3 | 4 | #include 5 | 6 | class AbstractCommand : public QObject { 7 | Q_OBJECT 8 | Q_PROPERTY(bool canExecute READ canExecute NOTIFY canExecuteChanged) 9 | public: 10 | AbstractCommand(QObject *parent = 0):QObject(parent){} 11 | Q_INVOKABLE virtual void execute() = 0; 12 | virtual bool canExecute() const = 0; 13 | signals: 14 | void canExecuteChanged(bool canExecute); 15 | }; 16 | 17 | #endif // ABSTRACT_COMMAND_H 18 | -------------------------------------------------------------------------------- /Examples/Example.pro: -------------------------------------------------------------------------------- 1 | CONFIG += c++11 qt 2 | QT += qml 3 | TARGET = example 4 | TEMPLATE = app 5 | INCLUDEPATH += .. 6 | SOURCES += Example.cpp \ 7 | asynchellocommand.cpp \ 8 | viewmodel.cpp 9 | OTHER_FILES = ../qmldir . 10 | unix { 11 | target.path = /usr/lib 12 | INSTALLS += target 13 | } 14 | DEFINES += SODIR=\\\"$$OUT_PWD/../MVVM/\\\" 15 | DEFINES += QMLDIR=\\\"$$PWD/../MVVM\\\" 16 | LIBS += -L$$OUT_PWD/../MVVM -lmvvmplugin 17 | RESOURCES += qml.qrc 18 | 19 | HEADERS += \ 20 | viewmodel.h \ 21 | asynchellocommand.h \ 22 | examplelistclass.h 23 | -------------------------------------------------------------------------------- /Examples/asynchellocommand.h: -------------------------------------------------------------------------------- 1 | #ifndef ASYNCHELLOCOMMAND_H 2 | #define ASYNCHELLOCOMMAND_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | class AsyncHelloCommand: public AbstractAsyncCommand 10 | { 11 | Q_OBJECT 12 | public: 13 | AsyncHelloCommand(QObject *parent=0); 14 | bool canExecute() const Q_DECL_OVERRIDE { return true; } 15 | QFuture executeAsync() Q_DECL_OVERRIDE; 16 | private: 17 | void* executeAsyncTaskFunc(); 18 | QSharedPointer> current; 19 | QMutex mutex; 20 | }; 21 | 22 | #endif // ASYNCHELLOCOMMAND_H 23 | -------------------------------------------------------------------------------- /Examples/viewmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "viewmodel.h" 2 | 3 | void ViewModel::resetObjects( ) 4 | { 5 | foreach ( ExampleListClass *mobject, pobjects ) { 6 | mobject->deleteLater(); 7 | } 8 | pobjects.clear(); 9 | emit objectsChanged(); 10 | } 11 | 12 | void ViewModel::appendObject( ExampleListClass *object ) 13 | { 14 | object->setParent( this ); 15 | pobjects.append( object ); 16 | emit objectsChanged(); 17 | } 18 | 19 | void ViewModel::deleteObject( ExampleListClass *object ) 20 | { 21 | if (pobjects.contains( object )) { 22 | pobjects.removeOne( object ); 23 | object->deleteLater(); 24 | emit objectsChanged(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MVVM/Commands/EmitCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef EMIT_COMMAND_H 2 | #define EMIT_COMMAND_H 3 | 4 | #include 5 | 6 | class EmitCommand : public AbstractCommand { 7 | Q_OBJECT 8 | Q_PROPERTY(bool canExecute READ canExecute WRITE setCanExecute NOTIFY privateCanExecuteChanged) 9 | public: 10 | EmitCommand(QObject *parent=0):AbstractCommand(parent){} 11 | 12 | void execute() Q_DECL_OVERRIDE; 13 | bool canExecute() const Q_DECL_OVERRIDE; 14 | public slots: 15 | void setCanExecute(bool canExecute); 16 | signals: 17 | void executes(); 18 | void privateCanExecuteChanged(); 19 | private: 20 | bool canExe = false; 21 | }; 22 | 23 | #endif // EMIT_COMMAND_H 24 | -------------------------------------------------------------------------------- /Examples/examplelistclass.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLELISTCLASS_H 2 | #define EXAMPLELISTCLASS_H 3 | 4 | #include 5 | 6 | class ExampleListClass: public QObject 7 | { 8 | Q_OBJECT 9 | 10 | Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) 11 | public: 12 | ExampleListClass(const QString& title, QObject *parent=0) 13 | : QObject(parent) 14 | , ptitle(title){ } 15 | 16 | const QString title() const { return ptitle; } 17 | void setTitle(const QString& title) { 18 | ptitle = title; emit titleChanged(); 19 | } 20 | 21 | signals: 22 | void titleChanged(); 23 | 24 | private: 25 | QString ptitle; 26 | }; 27 | 28 | #endif // EXAMPLELISTCLASS_H 29 | -------------------------------------------------------------------------------- /MVVM/Commands/CommandProxy.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMAND_PROXY_H 2 | #define COMMAND_PROXY_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | class CommandProxy:public AbstractCommand { 9 | Q_OBJECT 10 | public: 11 | CommandProxy(QObject *parent = 0); 12 | CommandProxy(const CommandProxy &other); 13 | CommandProxy(QSharedPointer &cmd, QObject *parent = 0); 14 | CommandProxy &operator=(const CommandProxy &other); 15 | void execute() Q_DECL_OVERRIDE; 16 | bool canExecute() const Q_DECL_OVERRIDE; 17 | private: 18 | QSharedPointer cmd; 19 | }; 20 | 21 | Q_DECLARE_METATYPE(CommandProxy) 22 | 23 | #endif // COMMAND_PROXY_H 24 | -------------------------------------------------------------------------------- /MVVM/Models/QmlQObjectList.h: -------------------------------------------------------------------------------- 1 | #ifndef QMLQOBJECTLIST_H 2 | #define QMLQOBJECTLIST_H 3 | 4 | #define DECLARE_QML_QOBJECT_LIST(type, name, owner, prop) \ 5 | QQmlListProperty name(){ \ 6 | return QQmlListProperty( \ 7 | this, 0,&owner::count ## type ## For ## name ## List, \ 8 | &owner::at ## type ## For ## name ## List); \ 9 | } \ 10 | static int count ## type ## For ## name ## List(QQmlListProperty*property){ \ 11 | owner *m = qobject_cast(property->object); \ 12 | return m->prop.size(); \ 13 | } \ 14 | static type *at ## type ## For ## name ## List( \ 15 | QQmlListProperty*property, int index){ \ 16 | owner *m = qobject_cast(property->object); \ 17 | return m->prop[index]; \ 18 | } 19 | 20 | #endif // QMLQOBJECTLIST_H 21 | -------------------------------------------------------------------------------- /Examples/Example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "viewmodel.h" 9 | #include "examplelistclass.h" 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | QGuiApplication app(argc, argv); 14 | 15 | qmlRegisterUncreatableType("Example", 1, 0, 16 | "ExampleListClass", ""); 17 | qmlRegisterType("Example", 1, 0, "ViewModel"); 18 | 19 | QQmlApplicationEngine engine; 20 | 21 | engine.addImportPath("qrc:/"); 22 | engine.addImportPath(QMLDIR); 23 | engine.addPluginPath(SODIR); 24 | 25 | engine.load(QUrl(QStringLiteral("qrc:/Example.qml"))); 26 | return app.exec(); 27 | } 28 | -------------------------------------------------------------------------------- /MVVM/Commands/RelayCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef DELEGATE_COMMAND_H 2 | #define DELEGATE_COMMAND_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | class RelayCommand : public AbstractCommand { 11 | Q_OBJECT 12 | public: 13 | RelayCommand(std::function executeDelegatep, 14 | std::function canExecuteDelegatep, 15 | QObject *parent = 0) 16 | : AbstractCommand(parent) 17 | , executeDelegate(executeDelegatep) 18 | , canExecuteDelegate(canExecuteDelegatep) {} 19 | 20 | void execute() Q_DECL_OVERRIDE; 21 | bool canExecute() const Q_DECL_OVERRIDE; 22 | public slots: 23 | void evaluateCanExecute(); 24 | private: 25 | std::function executeDelegate; 26 | std::function canExecuteDelegate; 27 | }; 28 | 29 | #endif // DELEGATE_COMMAND_H 30 | -------------------------------------------------------------------------------- /MVVM/Commands/AbstractAsyncCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTASYNCCOMMAND_H 2 | #define ABSTRACTASYNCCOMMAND_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | class AbstractAsyncCommand : public AbstractCommand { 10 | Q_OBJECT 11 | public: 12 | AbstractAsyncCommand(QObject *parent=0); 13 | 14 | Q_INVOKABLE virtual QFuture executeAsync() = 0; 15 | virtual void execute() Q_DECL_OVERRIDE; 16 | signals: 17 | void executeFinished(void* result); 18 | void executeProgressed(int value, int maximum); 19 | protected: 20 | QSharedPointer> start(); 21 | void progress(QSharedPointer> fut, int value, int total); 22 | void finish(QSharedPointer> fut, void* result); 23 | private: 24 | QVector>> m_futures; 25 | }; 26 | #endif 27 | -------------------------------------------------------------------------------- /MVVM/MVVM.pro: -------------------------------------------------------------------------------- 1 | CONFIG += c++11 qt plugin 2 | QT += qml 3 | TARGET = mvvmplugin 4 | TEMPLATE = lib 5 | INCLUDEPATH += .. 6 | SOURCES += \ 7 | Commands/CompositeCommand.cpp \ 8 | Commands/ListCommand.cpp \ 9 | Commands/EmitCommand.cpp \ 10 | Commands/CommandProxy.cpp \ 11 | Models/CommandListModel.cpp \ 12 | MvvmPlugin.cpp \ 13 | Commands/RelayCommand.cpp \ 14 | Commands/AbstractAsyncCommand.cpp 15 | HEADERS += \ 16 | Commands/AbstractCommand.h \ 17 | Commands/CompositeCommand.h \ 18 | Commands/ListCommand.h \ 19 | Commands/EmitCommand.h \ 20 | Commands/CommandProxy.h \ 21 | Models/CommandListModel.h \ 22 | MvvmPlugin.h \ 23 | Commands/RelayCommand.h \ 24 | Commands/AbstractAsyncCommand.h \ 25 | Models/QmlQObjectList.h 26 | OTHER_FILES = be/codeminded/mvvm/qmldir Examples/* 27 | unix { 28 | target.path = /usr/lib 29 | INSTALLS += target 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /MVVM/MvvmPlugin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | void MvvmPlugin::registerTypes(const char* uri) 17 | { 18 | qmlRegisterType(); 19 | 20 | qmlRegisterUncreatableType(uri, 1,0, "AbstractCommand", "uncreatable"); 21 | 22 | qmlRegisterType(uri, 1,0, "CommandProxy"); 23 | qmlRegisterType(uri, 1,0, "CompositeCommand"); 24 | qmlRegisterUncreatableType(uri, 1,0, "DelegateCommand", "uncreatable"); 25 | qmlRegisterType(uri, 1,0, "EmitCommand"); 26 | qmlRegisterType(uri, 1,0, "ListCommand"); 27 | 28 | qmlRegisterType(uri, 1,0, "CommandListModel"); 29 | } 30 | -------------------------------------------------------------------------------- /MVVM/Commands/ListCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_COMMAND_H 2 | #define LIST_COMMAND_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | class ListCommand : public AbstractCommand{ 9 | Q_OBJECT 10 | Q_PROPERTY(AbstractCommand *command READ command WRITE setCommand NOTIFY commandChanged) 11 | Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) 12 | public: 13 | ListCommand(QObject *parent = 0):AbstractCommand(parent){} 14 | ListCommand(const QSharedPointer &newCommand, QObject *parent = 0); 15 | 16 | void execute() Q_DECL_OVERRIDE; 17 | bool canExecute() const Q_DECL_OVERRIDE; 18 | AbstractCommand* command() const; 19 | void setCommand(AbstractCommand *newCommand); 20 | void setCommand(const QSharedPointer &newCommand); 21 | QString text() const; 22 | void setText(const QString &newValue); 23 | signals: 24 | void commandChanged(); 25 | void textChanged(); 26 | private: 27 | QSharedPointer cmd; 28 | QString txt; 29 | }; 30 | 31 | #endif // LIST_COMMAND_H 32 | -------------------------------------------------------------------------------- /MVVM/Models/CommandListModel.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMANDLISTMODEL_H 2 | #define COMMANDLISTMODEL_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | class CommandListModel:public QObject { 10 | Q_OBJECT 11 | Q_PROPERTY(QQmlListProperty commands READ commands NOTIFY commandsChanged ) 12 | Q_CLASSINFO("DefaultProperty", "commands") 13 | public: 14 | CommandListModel(QObject *parent = 0):QObject(parent){} 15 | void clearCommands(); 16 | int commandCount() const; 17 | QQmlListProperty commands(); 18 | void appendCommand(ListCommand *command); 19 | ListCommand* command(int idx) const; 20 | signals: 21 | void commandsChanged(); 22 | private: 23 | static void appendCommand(QQmlListProperty *lst, ListCommand *cmd); 24 | static ListCommand* command(QQmlListProperty *lst, int idx); 25 | static void clearCommands(QQmlListProperty *lst); 26 | static int commandCount(QQmlListProperty *lst); 27 | 28 | QList cmds; 29 | }; 30 | 31 | #endif // COMMANDLISTMODEL_H 32 | -------------------------------------------------------------------------------- /Examples/asynchellocommand.cpp: -------------------------------------------------------------------------------- 1 | #include "asynchellocommand.h" 2 | 3 | #include 4 | 5 | AsyncHelloCommand::AsyncHelloCommand(QObject* parent) 6 | : AbstractAsyncCommand(parent) { } 7 | 8 | void* AsyncHelloCommand::executeAsyncTaskFunc() 9 | { 10 | for (int i=0; i<10; i++) { 11 | QThread::sleep(1); 12 | qDebug() << "Hello Async!"; 13 | mutex.lock(); 14 | progress(current, i, 10); 15 | mutex.unlock(); 16 | } 17 | return nullptr; 18 | } 19 | 20 | QFuture AsyncHelloCommand::executeAsync() 21 | { 22 | mutex.lock(); 23 | current = start(); 24 | QFutureWatcher* watcher = new QFutureWatcher(this); 25 | connect(watcher, &QFutureWatcher::progressValueChanged, this, [=]{ 26 | mutex.lock(); 27 | progress(current, watcher->progressValue(), watcher->progressMaximum()); 28 | mutex.unlock(); 29 | }); 30 | connect(watcher, &QFutureWatcher::finished, this, [=]{ 31 | void* result=watcher->result(); 32 | mutex.lock(); 33 | finish(current, result); 34 | mutex.unlock(); 35 | watcher->deleteLater(); 36 | }); 37 | watcher->setFuture(QtConcurrent::run(this, &AsyncHelloCommand::executeAsyncTaskFunc)); 38 | QFuture future = current->future(); 39 | mutex.unlock(); 40 | 41 | return future; 42 | } 43 | -------------------------------------------------------------------------------- /MVVM/Commands/CommandProxy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "CommandProxy.h" 4 | 5 | CommandProxy::CommandProxy(QObject *parent) 6 | : AbstractCommand(parent) 7 | { 8 | QQmlEngine::setObjectOwnership (this, QQmlEngine::JavaScriptOwnership); 9 | } 10 | 11 | CommandProxy::CommandProxy(const CommandProxy &other) 12 | : AbstractCommand(nullptr) 13 | , cmd (other.cmd) 14 | { 15 | QQmlEngine::setObjectOwnership (this, QQmlEngine::JavaScriptOwnership); 16 | connect(cmd.data(), &AbstractCommand::canExecuteChanged, 17 | this, &AbstractCommand::canExecuteChanged); 18 | } 19 | 20 | CommandProxy::CommandProxy(QSharedPointer &proxied, QObject *parent) 21 | : AbstractCommand(parent) 22 | , cmd (proxied) 23 | { 24 | QQmlEngine::setObjectOwnership (this, QQmlEngine::JavaScriptOwnership); 25 | connect(proxied.data(), &AbstractCommand::canExecuteChanged, 26 | this, &AbstractCommand::canExecuteChanged); 27 | } 28 | 29 | void CommandProxy::execute() 30 | { 31 | cmd->execute(); 32 | } 33 | 34 | bool CommandProxy::canExecute() const 35 | { 36 | return cmd->canExecute(); 37 | } 38 | 39 | CommandProxy &CommandProxy::operator=(const CommandProxy &other) 40 | { 41 | disconnect(cmd.data(), &AbstractCommand::canExecuteChanged, 42 | this, &AbstractCommand::canExecuteChanged); 43 | cmd = other.cmd; 44 | connect(cmd.data(), &AbstractCommand::canExecuteChanged, 45 | this, &AbstractCommand::canExecuteChanged); 46 | return *this; 47 | } 48 | -------------------------------------------------------------------------------- /MVVM/Commands/CompositeCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPOSITE_COMMAND_H 2 | #define COMPOSITE_COMMAND_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | class CompositeCommand : public AbstractCommand { 11 | Q_OBJECT 12 | 13 | Q_PROPERTY(QQmlListProperty commands READ commands NOTIFY commandsChanged ) 14 | Q_CLASSINFO("DefaultProperty", "commands") 15 | public: 16 | CompositeCommand(QObject *parent = 0):AbstractCommand (parent) {} 17 | CompositeCommand(QList > cmds, QObject *parent=0); 18 | ~CompositeCommand(); 19 | void execute() Q_DECL_OVERRIDE; 20 | bool canExecute() const Q_DECL_OVERRIDE; 21 | void remove(const QSharedPointer &cmd); 22 | void add(const QSharedPointer &cmd); 23 | 24 | void add(AbstractCommand *cmd); 25 | void clearCommands(); 26 | QQmlListProperty commands(); 27 | 28 | signals: 29 | void commandsChanged(); 30 | private slots: 31 | void onCanExecuteChanged(bool canExecute); 32 | private: 33 | QList > cmds; 34 | static void appendCommand(QQmlListProperty *lst, AbstractCommand *cmd); 35 | static AbstractCommand* command(QQmlListProperty *lst, int idx); 36 | static void clearCommands(QQmlListProperty *lst); 37 | static int commandCount(QQmlListProperty *lst); 38 | }; 39 | 40 | #endif // COMPOSITE_COMMAND_H 41 | -------------------------------------------------------------------------------- /MVVM/Models/CommandListModel.cpp: -------------------------------------------------------------------------------- 1 | #include "CommandListModel.h" 2 | 3 | QQmlListProperty CommandListModel::commands() 4 | { 5 | return QQmlListProperty(this, this, 6 | &CommandListModel::appendCommand, 7 | &CommandListModel::commandCount, 8 | &CommandListModel::command, 9 | &CommandListModel::clearCommands); 10 | } 11 | 12 | void CommandListModel::appendCommand(ListCommand *command) 13 | { 14 | cmds.append( command ); 15 | emit commandsChanged(); 16 | } 17 | 18 | int CommandListModel::commandCount() const 19 | { 20 | return cmds.count(); 21 | } 22 | 23 | ListCommand *CommandListModel::command(int idx) const 24 | { 25 | return cmds.at(idx); 26 | } 27 | 28 | void CommandListModel::clearCommands() 29 | { 30 | cmds.clear(); 31 | emit commandsChanged(); 32 | } 33 | 34 | void CommandListModel::appendCommand(QQmlListProperty *lst, ListCommand *cmd) 35 | { 36 | reinterpret_cast(lst->data)->appendCommand(cmd); 37 | } 38 | 39 | int CommandListModel::commandCount(QQmlListProperty *lst) 40 | { 41 | return reinterpret_cast(lst->data)->commandCount(); 42 | } 43 | 44 | ListCommand *CommandListModel::command(QQmlListProperty *lst, int idx) 45 | { 46 | return reinterpret_cast(lst->data)->command(idx); 47 | } 48 | 49 | void CommandListModel::clearCommands(QQmlListProperty *lst) 50 | { 51 | reinterpret_cast(lst->data)->clearCommands(); 52 | } 53 | -------------------------------------------------------------------------------- /MVVM/Commands/ListCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ListCommand.h" 4 | 5 | ListCommand::ListCommand(const QSharedPointer &newCommand, QObject *parent) 6 | :AbstractCommand(parent) 7 | { 8 | setCommand(newCommand); 9 | } 10 | 11 | bool ListCommand::canExecute() const 12 | { 13 | if(!cmd.isNull()) 14 | return cmd->canExecute(); 15 | return false; 16 | } 17 | 18 | void ListCommand::execute() 19 | { 20 | if(!cmd.isNull()) 21 | cmd->execute(); 22 | } 23 | 24 | AbstractCommand* ListCommand::command() const 25 | { 26 | return cmd.data(); 27 | } 28 | 29 | void ListCommand::setCommand(const QSharedPointer &newCommand) 30 | { 31 | if(cmd.data() != newCommand) { 32 | cmd = newCommand; 33 | emit commandChanged(); 34 | if(newCommand != nullptr) 35 | connect(cmd.data(), &AbstractCommand::canExecuteChanged, 36 | this, &AbstractCommand::canExecuteChanged); 37 | emit canExecuteChanged(cmd->canExecute()); 38 | } 39 | } 40 | 41 | void ListCommand::setCommand(AbstractCommand *newCommand) 42 | { 43 | if(cmd.data() != newCommand) { 44 | cmd.reset(newCommand); 45 | QQmlEngine::setObjectOwnership (newCommand, QQmlEngine::CppOwnership); 46 | emit commandChanged(); 47 | if(newCommand != nullptr) 48 | connect(cmd.data(), &AbstractCommand::canExecuteChanged, 49 | this, &AbstractCommand::canExecuteChanged); 50 | emit canExecuteChanged(cmd->canExecute()); 51 | } 52 | } 53 | 54 | QString ListCommand::text() const 55 | { 56 | return txt; 57 | } 58 | 59 | void ListCommand::setText(const QString &newValue) 60 | { 61 | if(txt != newValue) { 62 | txt = newValue; 63 | emit textChanged(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /MVVM/Commands/AbstractAsyncCommand.cpp: -------------------------------------------------------------------------------- 1 | #include "AbstractAsyncCommand.h" 2 | 3 | /* Example: 4 | * QFuture AnAsyncCommand::executeAsync() 5 | * { 6 | * QSharedPointer> fut = start(); 7 | * QFutureWatcher* watcher = new QFutureWatcher(this); 8 | * connect(watcher, &QFutureWatcher::progressValueChanged, this, [=]{ 9 | * progress(fut, watcher->progressValue(), watcher->progressMaximum()); 10 | * }); 11 | * connect(watcher, &QFutureWatcher::finished, this, [=]{ 12 | * voud* result=watcher->result(); 13 | * finish(fut, result); 14 | * watcher->deleteLater(); 15 | * }); 16 | * watcher->setFuture(m_asyncSrc->performAsync()); 17 | * return fut->future(); 18 | * } */ 19 | 20 | AbstractAsyncCommand::AbstractAsyncCommand(QObject *parent) 21 | :AbstractCommand(parent) 22 | { 23 | m_futures.push_back(QSharedPointer>(new QFutureInterface)); 24 | } 25 | 26 | QSharedPointer> AbstractAsyncCommand::start() 27 | { 28 | QSharedPointer> fut = m_futures.last(); 29 | m_futures.push_back(QSharedPointer>(new QFutureInterface)); 30 | fut->reportStarted(); 31 | return fut; 32 | } 33 | 34 | void AbstractAsyncCommand::execute() 35 | { 36 | executeAsync(); 37 | } 38 | 39 | void AbstractAsyncCommand::finish(QSharedPointer> fut, void* result) 40 | { 41 | if (m_futures.contains(fut)) 42 | m_futures.removeAll(fut); 43 | emit executeFinished(result); 44 | fut->reportFinished(&result); 45 | } 46 | 47 | void AbstractAsyncCommand::progress(QSharedPointer> fut, int value, int total) 48 | { 49 | if (m_futures.contains(fut)) { 50 | fut->setProgressRange(0, total); 51 | fut->setProgressValue(value); 52 | emit executeProgressed (value, total); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Examples/View.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.3 2 | import QtQuick.Window 2.0 3 | import QtQuick.Controls 1.2 4 | 5 | import be.codeminded.mvvm 1.0 6 | 7 | import Example 1.0 8 | 9 | Item { 10 | id: container 11 | property ViewModel viewModel: ViewModel {} 12 | 13 | Connections { 14 | target: viewModel.asyncHelloCommand 15 | onExecuteProgressed: { 16 | progressBar.height = 25 17 | progressBar.value = value 18 | progressBar.maximumValue = maximum 19 | } 20 | onExecuteFinished: { 21 | progressBar.height = 0 22 | } 23 | } 24 | 25 | ProgressBar { 26 | id: progressBar 27 | anchors.top: container.top 28 | anchors.left: container.left 29 | anchors.right: container.right 30 | height: 0 31 | } 32 | 33 | ListView { 34 | id: list 35 | anchors { 36 | top: progressBar.bottom 37 | left: parent.left 38 | right: parent.right 39 | } 40 | height: 200 41 | model: viewModel.objectList 42 | delegate: Rectangle { 43 | color: "red" 44 | height: 20 45 | width: 30 46 | Text { 47 | text: modelData.title 48 | } 49 | } 50 | } 51 | 52 | Button { 53 | id: syncButton 54 | text: "Sync command" 55 | anchors.top: list.bottom 56 | anchors.left: container.left 57 | anchors.right: container.right 58 | height: 20 59 | 60 | enabled: viewModel.helloCommand.canExecute 61 | onClicked: viewModel.helloCommand.execute() 62 | } 63 | 64 | Button { 65 | id: asyncButton 66 | text: "Async command" 67 | anchors.top: syncButton.bottom 68 | anchors.left: container.left 69 | anchors.right: container.right 70 | height: 20 71 | 72 | enabled: viewModel.asyncHelloCommand.canExecute 73 | onClicked: viewModel.asyncHelloCommand.execute() 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Examples/Example.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.3 2 | import QtQuick.Window 2.0 3 | import QtQuick.Controls 1.2 4 | 5 | import be.codeminded.mvvm 1.0 6 | 7 | import Example 1.0 8 | 9 | Window { 10 | width: 360 11 | height: 360 12 | visible: true 13 | 14 | View { 15 | id: view 16 | height: childrenRect.height 17 | anchors { 18 | top: parent.top 19 | left: parent.left 20 | right: parent.right 21 | } 22 | } 23 | 24 | ListView { 25 | id: listView 26 | anchors { 27 | top: view.bottom 28 | bottom: parent.bottom 29 | left: parent.left 30 | right: parent.right 31 | } 32 | 33 | delegate: Item { 34 | height: 20 35 | width: listView.width 36 | MouseArea { 37 | anchors.fill: parent 38 | onClicked: if (modelData.canExecute) modelData.execute() 39 | } 40 | Text { 41 | anchors.fill: parent 42 | text: modelData.text 43 | color: modelData.canExecute ? "black" : "grey" 44 | } 45 | } 46 | 47 | model: comsModel.commands 48 | 49 | property bool combineCanExecute: false 50 | 51 | CommandListModel { 52 | id: comsModel 53 | 54 | ListCommand { 55 | text: "Enable combined" 56 | command: EmitCommand { 57 | onExecutes: { console.warn( "Hello1"); 58 | listView.combineCanExecute=true; } 59 | canExecute: true 60 | } 61 | } 62 | 63 | ListCommand { 64 | text: "Disable combined" 65 | command: EmitCommand { 66 | onExecutes: { console.warn( "Hello2"); 67 | listView.combineCanExecute=false; } 68 | canExecute: true 69 | } 70 | } 71 | 72 | ListCommand { 73 | text: "Combined emit commands" 74 | command: CompositeCommand { 75 | EmitCommand { 76 | onExecutes: console.warn("Emit command 1"); 77 | canExecute: listView.combineCanExecute 78 | } 79 | EmitCommand { 80 | onExecutes: console.warn("Emit command 2"); 81 | canExecute: listView.combineCanExecute 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Examples/viewmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEWMODEL_H 2 | #define VIEWMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include "asynchellocommand.h" 20 | #include "examplelistclass.h" 21 | 22 | class ViewModel: public QObject 23 | { 24 | Q_OBJECT 25 | 26 | Q_PROPERTY(CommandProxy* helloCommand READ helloCommand CONSTANT) 27 | Q_PROPERTY(AbstractAsyncCommand* asyncHelloCommand READ asyncHelloCommand CONSTANT) 28 | 29 | Q_PROPERTY(const QList objects READ objects 30 | NOTIFY objectsChanged ) 31 | Q_PROPERTY( QQmlListProperty objectList READ objectList 32 | NOTIFY objectsChanged ) 33 | public: 34 | ViewModel(QObject *parent=0) 35 | : QObject(parent) 36 | , helloCmd(new CompositeCommand()) 37 | , asyncCmd(new AsyncHelloCommand ) 38 | { 39 | 40 | QSharedPointer cCmd = helloCmd.dynamicCast(); 41 | cCmd->add( new RelayCommand ([=] { 42 | qWarning() << "Hello1 from C++ RelayCommand " << objects().length(); 43 | appendObject(new ExampleListClass("Hello")); 44 | }, [=]{ return true; })); 45 | cCmd->add( new RelayCommand ([=] { qWarning() << "Hello2 from C++ RelayCommand"; }, 46 | [=]{ return true; })); 47 | proxyCmd = new CommandProxy (helloCmd); 48 | } 49 | 50 | CommandProxy* helloCommand() { 51 | return proxyCmd; 52 | } 53 | 54 | AbstractAsyncCommand* asyncHelloCommand() { 55 | return asyncCmd.data(); 56 | } 57 | 58 | const QList objects () 59 | { return pobjects; } 60 | Q_INVOKABLE void appendObject( ExampleListClass *object); 61 | Q_INVOKABLE void deleteObject( ExampleListClass *object); 62 | Q_INVOKABLE void resetObjects(); 63 | 64 | signals: 65 | void objectsChanged(); 66 | 67 | protected: 68 | DECLARE_QML_QOBJECT_LIST(ExampleListClass, objectList, ViewModel, pobjects) 69 | 70 | private: 71 | QSharedPointer helloCmd; 72 | QSharedPointer asyncCmd; 73 | CommandProxy *proxyCmd; 74 | QList pobjects; 75 | }; 76 | 77 | #endif // VIEWMODEL_H 78 | -------------------------------------------------------------------------------- /MVVM/Commands/CompositeCommand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "CompositeCommand.h" 4 | 5 | CompositeCommand::CompositeCommand(QList > cmds, QObject *parent) 6 | : AbstractCommand (parent) 7 | , cmds (cmds) 8 | { 9 | foreach (const QSharedPointer &cmd, cmds) { 10 | connect(cmd.data(), &AbstractCommand::canExecuteChanged, 11 | this, &CompositeCommand::onCanExecuteChanged); 12 | } 13 | } 14 | 15 | void CompositeCommand::execute() 16 | { 17 | foreach (const QSharedPointer &cmd, cmds) { 18 | cmd->execute(); 19 | } 20 | } 21 | 22 | CompositeCommand::~CompositeCommand() 23 | { 24 | foreach (const QSharedPointer &cmd, cmds) { 25 | disconnect(cmd.data(), &AbstractCommand::canExecuteChanged, 26 | this, &CompositeCommand::onCanExecuteChanged); 27 | } 28 | } 29 | 30 | bool CompositeCommand::canExecute() const 31 | { 32 | foreach (const QSharedPointer& cmd, cmds) { 33 | if (!cmd->canExecute()) 34 | return false; 35 | } 36 | return true; 37 | } 38 | 39 | void CompositeCommand::onCanExecuteChanged(bool canExecute) 40 | { 41 | bool newstate = canExecute; 42 | bool origstate = !canExecute; 43 | foreach (const QSharedPointer &cmd, cmds) { 44 | if (cmd.data()!=sender()) { 45 | newstate &= cmd->canExecute(); 46 | origstate &= cmd->canExecute(); 47 | } 48 | } 49 | if (newstate != origstate) 50 | emit canExecuteChanged(newstate); 51 | } 52 | 53 | void CompositeCommand::add(const QSharedPointer& cmd) 54 | { 55 | bool origCanExecute = canExecute(); 56 | connect(cmd.data(), &AbstractCommand::canExecuteChanged, 57 | this, &CompositeCommand::onCanExecuteChanged); 58 | cmds.append(cmd); 59 | bool newCanExecute = canExecute(); 60 | if(origCanExecute != newCanExecute) 61 | emit canExecuteChanged(newCanExecute); 62 | emit commandsChanged(); 63 | } 64 | 65 | void CompositeCommand::add(AbstractCommand *cmd) 66 | { 67 | bool origCanExecute = canExecute(); 68 | connect(cmd, &AbstractCommand::canExecuteChanged, 69 | this, &CompositeCommand::onCanExecuteChanged); 70 | QQmlEngine::setObjectOwnership ( cmd, QQmlEngine::CppOwnership ); 71 | cmds.append(QSharedPointer(cmd)); 72 | bool newCanExecute = canExecute(); 73 | if(origCanExecute != newCanExecute) 74 | emit canExecuteChanged(newCanExecute); 75 | emit commandsChanged(); 76 | } 77 | 78 | void CompositeCommand::remove(const QSharedPointer &cmd) 79 | { 80 | bool origCanExecute = canExecute(); 81 | disconnect(cmd.data(), &AbstractCommand::canExecuteChanged, 82 | this, &CompositeCommand::onCanExecuteChanged); 83 | bool newCanExecute = canExecute(); 84 | cmds.removeAll(cmd); 85 | if(origCanExecute != newCanExecute) 86 | emit canExecuteChanged(newCanExecute); 87 | emit commandsChanged(); 88 | } 89 | 90 | void CompositeCommand::clearCommands() 91 | { 92 | cmds.clear(); 93 | emit commandsChanged(); 94 | } 95 | 96 | QQmlListProperty CompositeCommand::commands() 97 | { 98 | return QQmlListProperty(this, this, 99 | &CompositeCommand::appendCommand, 100 | &CompositeCommand::commandCount, 101 | &CompositeCommand::command, 102 | &CompositeCommand::clearCommands); 103 | } 104 | 105 | void CompositeCommand::appendCommand(QQmlListProperty *lst, AbstractCommand *cmd) 106 | { 107 | reinterpret_cast(lst->data)->add(cmd); 108 | } 109 | 110 | int CompositeCommand::commandCount(QQmlListProperty *lst) 111 | { 112 | return reinterpret_cast(lst->data)->cmds.count(); 113 | } 114 | 115 | AbstractCommand* CompositeCommand::command(QQmlListProperty *lst, int idx) 116 | { 117 | return reinterpret_cast(lst->data)->cmds.at(idx).data(); 118 | } 119 | 120 | void CompositeCommand::clearCommands(QQmlListProperty *lst) 121 | { 122 | reinterpret_cast(lst->data)->clearCommands(); 123 | } 124 | --------------------------------------------------------------------------------