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