From 23e48e35e22e2e1803e8053e230b93b751019316 Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Thu, 1 Mar 2018 22:44:14 +0100 Subject: [PATCH] implemented central ds widgets ui --- .../DataSyncSampleCore/DataSyncSampleCore.pro | 10 +- .../DataSyncSampleCore/docker-compose.yaml | 22 ++ .../DataSyncSampleCore/samplecoreapp.cpp | 4 +- src/mvvmdatasynccore/accountmodel.cpp | 8 + src/mvvmdatasynccore/accountmodel.h | 1 + src/mvvmdatasynccore/datasyncviewmodel.cpp | 14 +- src/mvvmdatasynccore/datasyncviewmodel.h | 3 + src/mvvmdatasyncwidgets/datasyncwindow.cpp | 112 ++++++- src/mvvmdatasyncwidgets/datasyncwindow.h | 16 + src/mvvmdatasyncwidgets/datasyncwindow.ui | 316 +++++++++++++++++- src/mvvmdatasyncwidgets/datasyncwindow_p.h | 2 +- src/mvvmquick/quickpresenter.cpp | 4 +- src/mvvmwidgets/widgetspresenter.cpp | 26 +- 13 files changed, 512 insertions(+), 26 deletions(-) create mode 100644 examples/mvvmdatasynccore/DataSyncSampleCore/docker-compose.yaml diff --git a/examples/mvvmdatasynccore/DataSyncSampleCore/DataSyncSampleCore.pro b/examples/mvvmdatasynccore/DataSyncSampleCore/DataSyncSampleCore.pro index f0df04e..7a074bd 100644 --- a/examples/mvvmdatasynccore/DataSyncSampleCore/DataSyncSampleCore.pro +++ b/examples/mvvmdatasynccore/DataSyncSampleCore/DataSyncSampleCore.pro @@ -7,13 +7,15 @@ TARGET = DataSyncSampleCore HEADERS += \ samplecoreapp.h \ - sampleviewmodel.h \ - sampledata.h + sampleviewmodel.h \ + sampledata.h SOURCES += \ samplecoreapp.cpp \ - sampleviewmodel.cpp \ - sampledata.cpp + sampleviewmodel.cpp \ + sampledata.cpp + +OTHER_FILES += docker-compose.yaml target.path = $$[QT_INSTALL_EXAMPLES]/mvvmdatasynccore/$$TARGET INSTALLS += target diff --git a/examples/mvvmdatasynccore/DataSyncSampleCore/docker-compose.yaml b/examples/mvvmdatasynccore/DataSyncSampleCore/docker-compose.yaml new file mode 100644 index 0000000..0189d56 --- /dev/null +++ b/examples/mvvmdatasynccore/DataSyncSampleCore/docker-compose.yaml @@ -0,0 +1,22 @@ +version: '2.2' +services: + datasync_postgres: + container_name: datasync_postgres + image: postgres:latest + environment: + POSTGRES_PASSWORD: baum42 + PGDATA: /var/lib/postgresql/data/pgdata + volumes: + - datasync_postgres_data:/var/lib/postgresql/data/pgdata + datasync_qdsapp: + container_name: datasync_qdsapp + image: skycoder42/qdsapp:latest + depends_on: + - datasync_postgres + ports: + - "4242:4242/tcp" + environment: + QDSAPP_DATABASE_HOST: datasync_postgres + QDSAPP_DATABASE_PASSWORD: baum42 +volumes: + datasync_postgres_data: diff --git a/examples/mvvmdatasynccore/DataSyncSampleCore/samplecoreapp.cpp b/examples/mvvmdatasynccore/DataSyncSampleCore/samplecoreapp.cpp index a185677..b6cb79a 100644 --- a/examples/mvvmdatasynccore/DataSyncSampleCore/samplecoreapp.cpp +++ b/examples/mvvmdatasynccore/DataSyncSampleCore/samplecoreapp.cpp @@ -26,7 +26,9 @@ int SampleCoreApp::startApp(const QStringList &arguments) setup = arguments.value(1); try { - QtDataSync::Setup().create(setup); + QtDataSync::Setup() + .setRemoteConfiguration(QUrl(QStringLiteral("ws://localhost:4242"))) + .create(setup); show(); return EXIT_SUCCESS; } catch (QException &e) { diff --git a/src/mvvmdatasynccore/accountmodel.cpp b/src/mvvmdatasynccore/accountmodel.cpp index ed56cde..a367fa2 100644 --- a/src/mvvmdatasynccore/accountmodel.cpp +++ b/src/mvvmdatasynccore/accountmodel.cpp @@ -73,6 +73,14 @@ int AccountModel::rowCount(const QModelIndex &parent) const return d->devices.size(); } +int AccountModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + else + return 2; +} + QVariant AccountModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) diff --git a/src/mvvmdatasynccore/accountmodel.h b/src/mvvmdatasynccore/accountmodel.h index 4aa8d56..487c6c0 100644 --- a/src/mvvmdatasynccore/accountmodel.h +++ b/src/mvvmdatasynccore/accountmodel.h @@ -29,6 +29,7 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Q_INVOKABLE bool removeDevice(const QModelIndex &index); diff --git a/src/mvvmdatasynccore/datasyncviewmodel.cpp b/src/mvvmdatasynccore/datasyncviewmodel.cpp index ac524d4..5096557 100644 --- a/src/mvvmdatasynccore/datasyncviewmodel.cpp +++ b/src/mvvmdatasynccore/datasyncviewmodel.cpp @@ -102,6 +102,15 @@ QString DataSyncViewModel::formatFingerPrint(const QByteArray &fingerPrint) return QString::fromUtf8(res.join(':')); } +void DataSyncViewModel::syncOrConnect() +{ + if(d->syncManager->syncState() == SyncManager::Disconnected || + d->syncManager->syncState() == SyncManager::Error) + d->syncManager->reconnect(); + else + d->syncManager->synchronize(); +} + void DataSyncViewModel::showDeviceInfo() { Q_UNIMPLEMENTED(); @@ -192,8 +201,8 @@ void DataSyncViewModel::performReset() MessageConfig config {MessageConfig::TypeMessageBox, MessageConfig::SubTypeQuestion}; config.setTitle(tr("Reset Account?")) .setText(tr("Do you want to reset your account? " - "You will loose the connection to all other devices and get a new identity." - "You can either keep your data or reset it as well." + "You will loose the connection to all other devices and get a new identity. " + "You can either keep your data or reset it as well. " "This cannot be undone!")) .setButtons(MessageConfig::YesToAll | MessageConfig::Yes | MessageConfig::Cancel) //TODO adjust to get ideal placement .setButtonText(MessageConfig::YesToAll, tr("Reset data")) @@ -324,6 +333,7 @@ void DataSyncViewModel::onInit(const QVariantHash ¶ms) emit syncManagerChanged(d->syncManager); emit accountManagerChanged(d->accountManager); + emit ready(); } catch(SetupDoesNotExistException &e) { logCritical() << "Failed to init DataSyncViewModel with error:" << e.what(); diff --git a/src/mvvmdatasynccore/datasyncviewmodel.h b/src/mvvmdatasynccore/datasyncviewmodel.h index 8bd1651..095ef99 100644 --- a/src/mvvmdatasynccore/datasyncviewmodel.h +++ b/src/mvvmdatasynccore/datasyncviewmodel.h @@ -51,6 +51,7 @@ public: Q_INVOKABLE static QString formatFingerPrint(const QByteArray &fingerPrint); public Q_SLOTS: + void syncOrConnect(); void showDeviceInfo(); void startExport(); void startImport(); @@ -73,6 +74,8 @@ Q_SIGNALS: void colorMapChanged(ColorMap colorMap); void statusStringChanged(); + void ready(); + protected: void onInit(const QVariantHash ¶ms) override; void onResult(quint32 requestCode, const QVariant &result) override; diff --git a/src/mvvmdatasyncwidgets/datasyncwindow.cpp b/src/mvvmdatasyncwidgets/datasyncwindow.cpp index a0b9733..020cc3a 100644 --- a/src/mvvmdatasyncwidgets/datasyncwindow.cpp +++ b/src/mvvmdatasyncwidgets/datasyncwindow.cpp @@ -1,20 +1,128 @@ #include "datasyncwindow.h" #include "datasyncwindow_p.h" #include "ui_datasyncwindow.h" + +#include + +#include + using namespace QtMvvm; +using namespace QtDataSync; DataSyncWindow::DataSyncWindow(QtMvvm::ViewModel *viewModel, QWidget *parent) : QWidget(parent, Qt::Window), - d(new DataSyncWindowPrivate(this, viewModel)) + d(new DataSyncWindowPrivate(viewModel)) { d->ui->setupUi(this); + + //ui setup + d->ui->identityButton->setDefaultAction(d->ui->actionEdit_Identity); + + d->ui->treeView->addActions({ + d->ui->actionRe_load_Device_List, + d->ui->action_Remove_Device + }); + + auto accountMenu = new QMenu(d->ui->accountButton); + accountMenu->addAction(d->ui->action_Network_exchange); + accountMenu->addAction(d->ui->action_Export_to_file); + accountMenu->addAction(d->ui->action_Import_from_file); + accountMenu->addSeparator(); + accountMenu->addAction(d->ui->actionRe_load_Device_List); + accountMenu->addAction(d->ui->action_Remove_Device); + accountMenu->addSeparator(); + accountMenu->addAction(d->ui->actionUpdate_Exchange_Key); + accountMenu->addSeparator(); + accountMenu->addAction(d->ui->action_Change_Remote_Server); + accountMenu->addAction(d->ui->action_Reset_Identity); + d->ui->accountButton->setMenu(accountMenu); + + //viewmodel stuff + connect(d->viewModel, &DataSyncViewModel::ready, + this, &DataSyncWindow::viewModelReady); } DataSyncWindow::~DataSyncWindow() {} +double DataSyncWindow::syncProgress() const +{ + return d->ui->progressBar->value() / 1000.0; +} + +QString DataSyncWindow::errorText() const +{ + return d->ui->errorLabel->text(); +} + +void DataSyncWindow::setSyncProgress(double syncProgress) +{ + if(syncProgress < 0) + d->ui->progressBar->reset(); + else + d->ui->progressBar->setValue(static_cast(syncProgress * 1000)); +} + +void DataSyncWindow::setErrorText(QString errorText) +{ + d->ui->errorLabel->setText(errorText); + d->ui->stackedWidget->setCurrentIndex(errorText.isEmpty() ? 0 : 1); +} + +void DataSyncWindow::viewModelReady() +{ + //sync manager bindings + bind(d->viewModel->syncManager(), "syncEnabled", + d->ui->syncCheckBox, "checked"); + bind(d->viewModel, "statusString", + d->ui->statusLabel, "text", + Binding::OneWayToView); + bind(d->viewModel->syncManager(), "syncProgress", + this, "syncProgress", + Binding::OneWayToView); + bind(d->viewModel->syncManager(), "lastError", + this, "errorText", + Binding::OneWayToView); + //account manager bindings + d->ui->treeView->setModel(d->viewModel->accountModel()); + + //sync manager connections + connect(d->ui->syncButton, &QCommandLinkButton::clicked, + d->viewModel, &DataSyncViewModel::syncOrConnect); + //account manager connections + connect(d->ui->actionEdit_Identity, &QAction::triggered, + d->viewModel, &DataSyncViewModel::showDeviceInfo); + connect(d->ui->action_Export_to_file, &QAction::triggered, + d->viewModel, &DataSyncViewModel::startExport); + connect(d->ui->action_Import_from_file, &QAction::triggered, + d->viewModel, &DataSyncViewModel::startImport); + connect(d->ui->action_Reset_Identity, &QAction::triggered, + d->viewModel, &DataSyncViewModel::performReset); + connect(d->ui->action_Change_Remote_Server, &QAction::triggered, + d->viewModel, &DataSyncViewModel::changeRemote); + connect(d->ui->action_Network_exchange, &QAction::triggered, + d->viewModel, &DataSyncViewModel::startNetworkExchange); + connect(d->ui->actionUpdate_Exchange_Key, &QAction::triggered, + d->viewModel->accountManager(), &AccountManager::updateExchangeKey); + connect(d->ui->actionRe_load_Device_List, &QAction::triggered, + d->viewModel->accountManager(), &AccountManager::listDevices); + connect(d->ui->action_Remove_Device, &QAction::triggered, + this, &DataSyncWindow::removeCurrentDevice); + connect(d->ui->actionUpdate_Exchange_Key, &QAction::triggered, + d->ui->actionUpdate_Exchange_Key, [this](){ + d->ui->actionUpdate_Exchange_Key->setEnabled(false); + }); +} + +void DataSyncWindow::removeCurrentDevice() +{ + auto index = d->ui->treeView->currentIndex(); + if(index.isValid()) + d->viewModel->accountModel()->removeDevice(index); +} + // ------------- Private Implementation ------------- -DataSyncWindowPrivate::DataSyncWindowPrivate(DataSyncWindow *q_ptr, ViewModel *viewModel) : +DataSyncWindowPrivate::DataSyncWindowPrivate(ViewModel *viewModel) : viewModel(static_cast(viewModel)), ui(new Ui::DataSyncWindow()) {} diff --git a/src/mvvmdatasyncwidgets/datasyncwindow.h b/src/mvvmdatasyncwidgets/datasyncwindow.h index c47f598..c41f60c 100644 --- a/src/mvvmdatasyncwidgets/datasyncwindow.h +++ b/src/mvvmdatasyncwidgets/datasyncwindow.h @@ -16,10 +16,26 @@ class Q_MVVMDATASYNCWIDGETS_EXPORT DataSyncWindow : public QWidget { Q_OBJECT + Q_PROPERTY(double syncProgress READ syncProgress WRITE setSyncProgress) + Q_PROPERTY(QString errorText READ errorText WRITE setErrorText) + public: Q_INVOKABLE explicit DataSyncWindow(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr); ~DataSyncWindow(); + double syncProgress() const; + QString errorText() const; + +public Q_SLOTS: + void setSyncProgress(double syncProgress); + void setErrorText(QString errorText); + +protected Q_SLOTS: + virtual void viewModelReady(); + +private Q_SLOTS: + void removeCurrentDevice(); + private: QScopedPointer d; }; diff --git a/src/mvvmdatasyncwidgets/datasyncwindow.ui b/src/mvvmdatasyncwidgets/datasyncwindow.ui index 2204bca..0f296cb 100644 --- a/src/mvvmdatasyncwidgets/datasyncwindow.ui +++ b/src/mvvmdatasyncwidgets/datasyncwindow.ui @@ -1,21 +1,321 @@ + - - - DataSyncWindow - + 0 0 - 400 - 300 + 430 + 365 - Form + Synchronization + + + + + Synchronization &enabled + + + true + + + + + + + + + + 16 + 75 + true + + + + Qt::RichText + + + Qt::AlignCenter + + + + + + + &Synchronize + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1000 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 170 + 0 + 0 + + + + + + + + + 170 + 0 + 0 + + + + + + + + + 164 + 166 + 168 + + + + + + + + + 75 + true + + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + Qt::Horizontal + + + + + + + Qt::ActionsContextMenu + + + + + + + + + Qt::ToolButtonFollowStyle + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Account &actions + + + + .. + + + QToolButton::InstantPopup + + + Qt::ToolButtonFollowStyle + + + + + + + + + + :/de/skycoder42/qtmvvm/datasync/widgets/icons/import.ico:/de/skycoder42/qtmvvm/datasync/widgets/icons/import.ico + + + &Import from file + + + Ctrl+I + + + + + + :/de/skycoder42/qtmvvm/datasync/widgets/icons/export.ico:/de/skycoder42/qtmvvm/datasync/widgets/icons/export.ico + + + &Export to file + + + Ctrl+E + + + + + + :/de/skycoder42/qtmvvm/datasync/widgets/icons/exchange.ico:/de/skycoder42/qtmvvm/datasync/widgets/icons/exchange.ico + + + &Network exchange + + + Ctrl+N + + + + + + :/de/skycoder42/qtmvvm/datasync/widgets/icons/idreset.ico:/de/skycoder42/qtmvvm/datasync/widgets/icons/idreset.ico + + + Rese&t identity + + + + + + :/de/skycoder42/qtmvvm/datasync/widgets/icons/changeRemote.ico:/de/skycoder42/qtmvvm/datasync/widgets/icons/changeRemote.ico + + + &Change remote server + + + + + + .. + + + &Remove selected device + + + Del + + + + + + .. + + + &Identity + + + Ctrl+U + + + + + + .. + + + Update exchange &key + + + + + + .. + + + Re&load device list + + + Ctrl+R + + - + diff --git a/src/mvvmdatasyncwidgets/datasyncwindow_p.h b/src/mvvmdatasyncwidgets/datasyncwindow_p.h index ed3685e..184948b 100644 --- a/src/mvvmdatasyncwidgets/datasyncwindow_p.h +++ b/src/mvvmdatasyncwidgets/datasyncwindow_p.h @@ -13,7 +13,7 @@ namespace QtMvvm { class DataSyncWindowPrivate { public: - DataSyncWindowPrivate(DataSyncWindow *q_ptr, ViewModel *viewModel); + DataSyncWindowPrivate(ViewModel *viewModel); DataSyncViewModel *viewModel; QScopedPointer ui; diff --git a/src/mvvmquick/quickpresenter.cpp b/src/mvvmquick/quickpresenter.cpp index 846e3c0..1ce1361 100644 --- a/src/mvvmquick/quickpresenter.cpp +++ b/src/mvvmquick/quickpresenter.cpp @@ -106,7 +106,9 @@ void QuickPresenter::setInputViewFactory(InputViewFactory *inputViewFactory) QUrl QuickPresenter::findViewUrl(const QMetaObject *viewModelType) { auto currentMeta = viewModelType; - while(currentMeta && currentMeta->inherits(&ViewModel::staticMetaObject)) { + while(currentMeta && + currentMeta->inherits(&ViewModel::staticMetaObject) && + currentMeta != &ViewModel::staticMetaObject) { if(d->explicitMappings.contains(currentMeta)) return d->explicitMappings.value(currentMeta); else { diff --git a/src/mvvmwidgets/widgetspresenter.cpp b/src/mvvmwidgets/widgetspresenter.cpp index 59d3263..5fe7acf 100644 --- a/src/mvvmwidgets/widgetspresenter.cpp +++ b/src/mvvmwidgets/widgetspresenter.cpp @@ -132,19 +132,31 @@ void WidgetsPresenter::setInputWidgetFactory(InputWidgetFactory *inputWidgetFact const QMetaObject *WidgetsPresenter::findWidgetMetaObject(const QMetaObject *viewModelMetaObject) { auto currentMeta = viewModelMetaObject; - while(currentMeta && currentMeta->inherits(&ViewModel::staticMetaObject)) { + while(currentMeta && + currentMeta->inherits(&ViewModel::staticMetaObject) && + currentMeta != &ViewModel::staticMetaObject) { if(d->explicitMappings.contains(currentMeta)) return d->explicitMappings.value(currentMeta); else { QByteArray cName = currentMeta->className(); + //strip viewmodel auto lIndex = cName.lastIndexOf("ViewModel"); if(lIndex > 0) cName.truncate(lIndex); + //strip namespaces + lIndex = cName.lastIndexOf("::"); + if(lIndex > 0) + cName = cName.mid(lIndex + 2); auto shortest = std::numeric_limits::max(); const QMetaObject *res = nullptr; for(auto metaObject : d->implicitMappings) { QByteArray vName = metaObject->className(); + //strip namespaces + lIndex = vName.lastIndexOf("::"); + if(lIndex > 0) + vName = vName.mid(lIndex + 2); + if(vName.startsWith(cName) && vName.size() < shortest) { shortest = vName.size(); res = metaObject; @@ -390,13 +402,13 @@ void WidgetsPresenter::presentFileDialog(const MessageConfig &config, QPointersetResult(QVariant::fromValue(dialog->selectedUrls())); - else - result->setResult(dialog->selectedUrls().first()); - if(resCode == QDialog::Accepted) + if(resCode == QDialog::Accepted) { + if(isMultiFile) + result->setResult(QVariant::fromValue(dialog->selectedUrls())); + else if(!dialog->selectedUrls().isEmpty()) + result->setResult(dialog->selectedUrls().first()); result->complete(MessageConfig::Ok); - else + } else result->complete(MessageConfig::Cancel); } });