├── .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 |
--------------------------------------------------------------------------------