diff --git a/src/imports/mvvmdatasyncquick/DataSyncView11.qml b/src/imports/mvvmdatasyncquick/DataSyncView11.qml
new file mode 100644
index 0000000..7963677
--- /dev/null
+++ b/src/imports/mvvmdatasyncquick/DataSyncView11.qml
@@ -0,0 +1,294 @@
+import QtQuick 2.10
+import QtQuick.Controls 2.3
+import QtQuick.Controls.Material 2.3
+import QtQuick.Controls.Universal 2.3
+import QtQuick.Layouts 1.3
+import de.skycoder42.QtDataSync 4.0
+import de.skycoder42.QtMvvm.Core 1.1
+import de.skycoder42.QtMvvm.Quick 1.1
+import de.skycoder42.QtMvvm.DataSync.Core 1.1
+
+/*! @brief The view implementation for the QtMvvm::DataSyncViewModel
+ *
+ * @extends QtQuick.Controls.Page
+ *
+ * @details This is the view used to present a datasync view model. You can extend the class
+ * if you need to extend that view.
+ *
+ * @sa QtMvvm::DataSyncViewModel
+ */
+Page {
+ id: _dataSyncView
+
+ /*! @brief The viewmodel to use
+ *
+ * @default{Injected}
+ *
+ * @accessors{
+ * @memberAc{viewModel}
+ * @notifyAc{viewModelChanged()}
+ * }
+ *
+ * @sa QtMvvm::DataSyncViewModel
+ */
+ property DataSyncViewModel viewModel: null
+
+ header: ContrastToolBar {
+ id: _toolBar
+
+ RowLayout {
+ id: _toolLayout
+ anchors.fill: parent
+ spacing: 0
+
+ ToolBarLabel {
+ id: _titleLabel
+ Layout.fillWidth: true
+ text: qsTr("Synchronization")
+ }
+
+ ActionButton {
+ id: _syncButton
+ icon.name: "view-refresh"
+ icon.source: "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_sync.svg"
+ text: qsTr("Synchronize")
+ onClicked: viewModel.syncOrConnect()
+ }
+
+ ActionButton {
+ id: _idButton
+ icon.name: "fingerprint-gui"
+ icon.source: "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_fingerprint.svg"
+ text: qsTr("Edit Identity")
+ onClicked: viewModel.showDeviceInfo()
+ }
+
+ MenuButton {
+ id: _moreButton
+
+ MenuItem {
+ text: qsTr("Update exchange key")
+ onClicked: viewModel.accountManager.updateExchangeKey()
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ text: qsTr("Reload devices list")
+ onClicked: viewModel.accountManager.listDevices()
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ text: qsTr("Change remote server")
+ onClicked: viewModel.changeRemote()
+ }
+
+ MenuItem {
+ text: qsTr("Reset Identity")
+ onClicked: viewModel.performReset()
+ }
+ }
+ }
+ }
+
+ Pane {
+ anchors.fill: parent
+ ColumnLayout {
+ id: _layout
+ anchors.fill: parent
+ spacing: 16
+
+ Switch {
+ id: _syncSwitch
+ text: qsTr("Synchronization enabled")
+ Layout.fillWidth: true
+
+ MvvmBinding {
+ viewModel: _dataSyncView.viewModel.syncManager
+ viewModelProperty: "syncEnabled"
+ view: _syncSwitch
+ viewProperty: "checked"
+ }
+ }
+
+ Label {
+ id: _statusLabel
+ Layout.fillWidth: true
+ text: viewModel.statusString
+ font.bold: true
+ font.pointSize: 16
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ ProgressBar {
+ id: _syncProgress
+ Layout.fillWidth: true
+ from: 0
+ to: 1
+ value: viewModel.syncManager.syncProgress
+ visible: !_errorLabel.visible
+ }
+
+ Label {
+ id: _errorLabel
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ text: viewModel.syncManager.lastError
+ visible: text != ""
+ color: "#aa0000"
+ font.bold: true
+ }
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.minimumHeight: 1
+ Layout.maximumHeight: 1
+ color: {
+ if(QuickPresenter.currentStyle === "Material")
+ return Material.foreground;
+ else if(QuickPresenter.currentStyle === "Universal")
+ return Universal.foreground;
+ else
+ return "black";
+ }
+ }
+
+ Label {
+ Layout.fillWidth: true
+ text: qsTr("Other Devices:")
+ }
+
+ ScrollView {
+ id: _devicesScrollView
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ clip: true
+
+ ListView {
+ id: _devicesList
+
+ model: viewModel.sortedModel
+ delegate: SwipeDelegate {
+ id: _swipeDelegate
+ width: _devicesScrollView.width
+
+ contentItem: ColumnLayout {
+ id: _delegateLayout
+ spacing: 8
+
+ Label {
+ id: _nameLabel
+ Layout.fillWidth: true
+ text: name
+ }
+
+ Label {
+ id: _fpLabel
+ font.pointSize: _nameLabel.font.pointSize * 0.8
+ Layout.fillWidth: true
+ Layout.leftMargin: 8
+ text: fingerPrint
+ elide: Text.ElideMiddle
+ opacity: 0.75
+ }
+ }
+
+ ListView.onRemove: SequentialAnimation {
+ PropertyAction {
+ target: _swipeDelegate
+ property: "ListView.delayRemove"
+ value: true
+ }
+ NumberAnimation {
+ target: _swipeDelegate
+ property: "height"
+ to: 0
+ easing.type: Easing.InOutQuad
+ }
+ PropertyAction {
+ target: _swipeDelegate
+ property: "ListView.delayRemove"
+ value: false
+ }
+ }
+
+ swipe.right: Rectangle {
+ height: _devicesScrollView.height
+ width: height
+ anchors.right: parent.right
+ color: {
+ if(QuickPresenter.currentStyle === "Material")
+ return Material.color(Material.Red);
+ else if(QuickPresenter.currentStyle === "Universal")
+ return Universal.color(Universal.Red);
+ else
+ return "#FF0000";
+ }
+
+ ActionButton {
+ anchors.centerIn: parent
+ implicitHeight: parent.height
+ implicitWidth: parent.width
+
+ icon.name: "user-trash"
+ icon.source: "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_delete_forever.svg"
+ text: qsTr("Remove Device")
+
+ Material.foreground: "white"
+ Universal.foreground: "white"
+
+ onClicked: {
+ _swipeDelegate.swipe.close();
+ viewModel.removeDevice(index)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RoundMenuButton {
+ id: _addButton
+
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 16
+
+ text: qsTr("Add new devices")
+ icon.name: checked ? "tab-close" : "list-add"
+ icon.source: checked ?
+ "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_close.svg" :
+ "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_add.svg"
+ stickyToolTips: true
+
+ Action {
+ text: qsTr("Network Exchange")
+ icon.name: "network-connect"
+ icon.source: "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_exchange.svg"
+
+ onTriggered: viewModel.startNetworkExchange()
+ }
+
+ Action {
+ text: qsTr("Export to file")
+ icon.name: "document-export"
+ icon.source: "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_export.svg"
+
+ onTriggered: viewModel.startExport()
+ }
+
+ Action {
+ text: qsTr("Import from file")
+ icon.name: "document-import"
+ icon.source: "qrc:/de/skycoder42/qtmvvm/quick/icons/ic_import.svg"
+
+ onTriggered: viewModel.startImport()
+ }
+ }
+}
diff --git a/src/imports/mvvmdatasyncquick/mvvmdatasyncquick.pro b/src/imports/mvvmdatasyncquick/mvvmdatasyncquick.pro
index 19e7858..abdb13c 100644
--- a/src/imports/mvvmdatasyncquick/mvvmdatasyncquick.pro
+++ b/src/imports/mvvmdatasyncquick/mvvmdatasyncquick.pro
@@ -15,6 +15,7 @@ SOURCES += \
QML_FILES += \
SubButton.qml \
DataSyncView.qml \
+ DataSyncView11.qml \
NetworkExchangeView.qml \
IdentityEditView.qml \
ExportSetupView.qml \
diff --git a/src/imports/mvvmdatasyncquick/qmldir b/src/imports/mvvmdatasyncquick/qmldir
index 62f9379..3336e06 100644
--- a/src/imports/mvvmdatasyncquick/qmldir
+++ b/src/imports/mvvmdatasyncquick/qmldir
@@ -12,4 +12,6 @@ ExportSetupView 1.0 ExportSetupView.qml
ChangeRemoteView 1.0 ChangeRemoteView.qml
DataSyncView 1.0 DataSyncView.qml
+DataSyncView 1.1 DataSyncView11.qml
+
NetworkExchangeView 1.0 NetworkExchangeView.qml
diff --git a/src/imports/mvvmquick/RoundMenuButton.qml b/src/imports/mvvmquick/RoundMenuButton.qml
new file mode 100644
index 0000000..42b752f
--- /dev/null
+++ b/src/imports/mvvmquick/RoundMenuButton.qml
@@ -0,0 +1,156 @@
+import QtQuick 2.10
+import QtQuick.Controls 2.3
+import de.skycoder42.QtMvvm.Quick 1.1
+
+Item {
+ id: _roundMenuButton
+
+ property alias text: _rootButton.text
+ property alias icon: _rootButton.icon
+ property alias checked: _rootButton.checked
+
+ property real buttonSpacing: 16
+ property real expansionAngle: 0
+ property size subButtonSize: Qt.size(40, 40)
+ property bool stickyToolTips: false
+ property bool invertToolTipDirection: Qt.application.layoutDirection == Qt.RightToLeft
+
+ readonly property alias rootButton: _rootButton
+
+ default property list actions
+
+ implicitWidth: _rootButton.implicitWidth
+ implicitHeight: _rootButton.implicitHeight
+
+ RoundActionButton {
+ id: _rootButton
+ z: 10
+ anchors.fill: parent
+ checkable: true
+ }
+
+ QtObject {
+ id: _p
+
+ function toRadians(angle) {
+ return angle * (Math.PI/180);
+ }
+
+ readonly property real vOffset: -1 * Math.cos(toRadians(_roundMenuButton.expansionAngle))
+ readonly property real hOffset: Math.sin(toRadians(_roundMenuButton.expansionAngle))
+ }
+
+ Repeater {
+ model: actions
+
+ delegate: RoundActionButton {
+ id: _subButton
+
+ highlighted: false
+ anchors.horizontalCenter: _rootButton.horizontalCenter
+ anchors.verticalCenter: _rootButton.verticalCenter
+ implicitHeight: _roundMenuButton.subButtonSize.height + padding
+ implicitWidth: _roundMenuButton.subButtonSize.width + padding
+ state: _rootButton.checked ? "expanded" : "collapsed"
+
+ toolTip: _roundMenuButton.stickyToolTips ? "" : _subButton.text
+
+ onClicked: _rootButton.checked = false
+
+ /* calc offsets as:
+ * - one subbutton (from center to center) + spacing between buttons
+ * - that times the number of buttons before this one + +1
+ * - that plus the extra height delta from the root button
+ * - that times the factor from the angle
+ */
+ readonly property real maxVOffset: _p.vOffset * ((1 + index) * (_subButton.height + _roundMenuButton.buttonSpacing) + (_rootButton.height - _subButton.height)/2)
+ readonly property real maxHOffset: _p.hOffset * ((1 + index) * (_subButton.width + _roundMenuButton.buttonSpacing) + (_rootButton.width - _subButton.width)/2)
+
+ action: modelData
+
+ ToolTip {
+ id: _permaToolTip
+ visible: _roundMenuButton.stickyToolTips && _subButton.text != "" && _subButton.visible
+ text: _subButton.text
+ x: invertToolTipDirection ?
+ _subButton.width + _roundMenuButton.buttonSpacing :
+ -(_permaToolTip.width + _roundMenuButton.buttonSpacing)
+ y: (_subButton.height - height)/2
+ }
+
+ states: [
+ State {
+ name: "collapsed"
+ PropertyChanges {
+ target: _subButton
+ anchors.verticalCenterOffset: 0
+ anchors.horizontalCenterOffset: 0
+ visible: false
+ }
+ },
+ State {
+ name: "expanded"
+ PropertyChanges {
+ target: _subButton
+ anchors.verticalCenterOffset: maxVOffset
+ anchors.horizontalCenterOffset: maxHOffset
+ visible: true
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "collapsed"
+ to: "expanded"
+ SequentialAnimation {
+ PropertyAnimation {
+ target: _subButton
+ property: "visible"
+ duration: 0
+ }
+ ParallelAnimation {
+ PropertyAnimation {
+ target: _subButton
+ property: "anchors.verticalCenterOffset"
+ duration: 250
+ easing.type: Easing.OutCubic
+ }
+ PropertyAnimation {
+ target: _subButton
+ property: "anchors.horizontalCenterOffset"
+ duration: 250
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ },
+ Transition {
+ from: "expanded"
+ to: "collapsed"
+ SequentialAnimation {
+ ParallelAnimation {
+ PropertyAnimation {
+ target: _subButton
+ property: "anchors.verticalCenterOffset"
+ duration: 250
+ easing.type: Easing.InCubic
+ }
+ PropertyAnimation {
+ target: _subButton
+ property: "anchors.horizontalCenterOffset"
+ duration: 250
+ easing.type: Easing.InCubic
+ }
+ }
+ PropertyAnimation {
+ target: _subButton
+ property: "visible"
+ duration: 0
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/src/imports/mvvmquick/SearchBar.qml b/src/imports/mvvmquick/SearchBar.qml
index c03dfef..b5e9d6b 100644
--- a/src/imports/mvvmquick/SearchBar.qml
+++ b/src/imports/mvvmquick/SearchBar.qml
@@ -2,7 +2,6 @@ import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.3
import QtQuick.Layouts 1.3
-import de.skycoder42.QtMvvm.Core 1.1
import de.skycoder42.QtMvvm.Quick 1.1
Item {
diff --git a/src/imports/mvvmquick/mvvmquick.pro b/src/imports/mvvmquick/mvvmquick.pro
index c7ca126..f460f49 100644
--- a/src/imports/mvvmquick/mvvmquick.pro
+++ b/src/imports/mvvmquick/mvvmquick.pro
@@ -50,7 +50,8 @@ QML_FILES += \
OverviewListView.qml \
SettingsView.qml \
SettingsView11.qml \
- SearchBar.qml
+ SearchBar.qml \
+ RoundMenuButton.qml
RESOURCES += \
qtmvvmquick_plugin.qrc
diff --git a/src/imports/mvvmquick/qmldir b/src/imports/mvvmquick/qmldir
index 57d5d99..4c7e47a 100644
--- a/src/imports/mvvmquick/qmldir
+++ b/src/imports/mvvmquick/qmldir
@@ -24,6 +24,7 @@ RoundActionButton 1.0 RoundActionButton.qml
MenuButton 1.0 MenuButton.qml
SearchBar 1.1 SearchBar.qml
+RoundMenuButton 1.1 RoundMenuButton.qml
PresenterProgress 1.0 PresenterProgress.qml
PresentingStackView 1.0 PresentingStackView.qml