diff --git a/app/icons/Download.svg b/app/icons/Download.svg new file mode 100644 index 000000000..b97db411c --- /dev/null +++ b/app/icons/Download.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Info.svg b/app/icons/Info.svg new file mode 100644 index 000000000..ecf9df01d --- /dev/null +++ b/app/icons/Info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/ProjectButtonMore.svg b/app/icons/ProjectButtonMore.svg new file mode 100644 index 000000000..d8773b76f --- /dev/null +++ b/app/icons/ProjectButtonMore.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/icons/Stop.svg b/app/icons/Stop.svg new file mode 100644 index 000000000..d022fd4ae --- /dev/null +++ b/app/icons/Stop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Sync.svg b/app/icons/Sync.svg new file mode 100644 index 000000000..9fa37fa99 --- /dev/null +++ b/app/icons/Sync.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/icons/icons.qrc b/app/icons/icons.qrc index abc0f7b21..8c0b4cec4 100644 --- a/app/icons/icons.qrc +++ b/app/icons/icons.qrc @@ -20,6 +20,11 @@ Done.svg Edit.svg More.svg + ProjectButtonMore.svg + Stop.svg + Download.svg + Info.svg + Sync.svg MorePhotos.svg diff --git a/app/mmstyle.h b/app/mmstyle.h index 473d586c4..aa4a7669c 100644 --- a/app/mmstyle.h +++ b/app/mmstyle.h @@ -93,6 +93,11 @@ class MMStyle: public QObject Q_PROPERTY( QUrl doneIcon READ doneIcon CONSTANT ) Q_PROPERTY( QUrl editIcon READ editIcon CONSTANT ) Q_PROPERTY( QUrl moreIcon READ moreIcon CONSTANT ) + Q_PROPERTY( QUrl projectButtonMoreIcon READ projectButtonMoreIcon CONSTANT ) + Q_PROPERTY( QUrl stopIcon READ stopIcon CONSTANT ) + Q_PROPERTY( QUrl syncIcon READ syncIcon CONSTANT ) + Q_PROPERTY( QUrl infoIcon READ infoIcon CONSTANT ) + Q_PROPERTY( QUrl downloadIcon READ downloadIcon CONSTANT ) Q_PROPERTY( QUrl morePhotosIcon READ morePhotosIcon CONSTANT ) // Images @@ -177,6 +182,11 @@ class MMStyle: public QObject QUrl doneIcon() {return QUrl( "qrc:/Done.svg" );} QUrl editIcon() {return QUrl( "qrc:/Edit.svg" );} QUrl moreIcon() {return QUrl( "qrc:/More.svg" );} + QUrl projectButtonMoreIcon() {return QUrl( "qrc:/ProjectButtonMore.svg" );} + QUrl stopIcon() {return QUrl( "qrc:/Stop.svg" );} + QUrl syncIcon() {return QUrl( "qrc:/Sync.svg" );} + QUrl infoIcon() {return QUrl( "qrc:/Info.svg" );} + QUrl downloadIcon() {return QUrl( "qrc:/Download.svg" );} QUrl morePhotosIcon() {return QUrl( "qrc:/MorePhotos.svg" );} QUrl uploadImage() {return QUrl( "qrc:/UploadImage.svg" );} diff --git a/app/qml/components/MMListDrawer.qml b/app/qml/components/MMListDrawer.qml new file mode 100644 index 000000000..2ac2bbf1c --- /dev/null +++ b/app/qml/components/MMListDrawer.qml @@ -0,0 +1,107 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +Drawer { + id: control + + property alias title: title.text + property alias model: listView.model + + padding: 20 * __dp + + signal clicked(type: string) + + width: window.width + height: mainColumn.height + edge: Qt.BottomEdge + + Rectangle { + color: roundedRect.color + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 2 * radius + anchors.topMargin: -radius + radius: 20 * __dp + } + + Rectangle { + id: roundedRect + + anchors.fill: parent + color: __style.whiteColor + + Column { + id: mainColumn + + width: parent.width + spacing: 20 * __dp + leftPadding: control.padding + rightPadding: control.padding + bottomPadding: control.padding + + Row { + width: parent.width - 2 * control.padding + anchors.horizontalCenter: parent.horizontalCenter + + Item { width: closeButton.width; height: 1 } + + Text { + id: title + + anchors.verticalCenter: parent.verticalCenter + font: __style.t2 + width: parent.width - closeButton.width * 2 + color: __style.forestColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + Image { + id: closeButton + + source: __style.closeButtonIcon + + MouseArea { + anchors.fill: parent + onClicked: control.visible = false + } + } + } + + ListView { + id: listView + + width: parent.width - 2 * control.padding + height: control.model ? control.model.count * __style.menuDrawerHeight : 0 + interactive: false + + delegate: Item { + width: listView.width + height: __style.menuDrawerHeight + + MMListDrawerItem { + width: listView.width + + type: model.type + text: model.name + iconSource: model.iconSource + + onClicked: function(type) { control.clicked(type); control.visible = false } + } + } + } + } + } +} diff --git a/app/qml/components/MMListDrawerItem.qml b/app/qml/components/MMListDrawerItem.qml new file mode 100644 index 000000000..1e4b1b7fd --- /dev/null +++ b/app/qml/components/MMListDrawerItem.qml @@ -0,0 +1,58 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +Item { + id: control + + signal clicked(type: string) + + required property string type + required property string text + required property var iconSource + + width: control.width + height: __style.menuDrawerHeight + + Rectangle { + anchors.top: parent.top + width: parent.width + height: 1 * __dp + color: __style.grayColor + } + + Row { + height: parent.height + width: parent.width + spacing: 10 * __dp + + MMIcon { + height: parent.height + width: 20 * __dp + color: __style.forestColor + source: control.iconSource ?? "" + } + + Text { + height: parent.height + verticalAlignment: Text.AlignVCenter + text: control.text + color: __style.nightColor + font: __style.t3 + } + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked(control.type) + } +} diff --git a/app/qml/components/MMProjectItem.qml b/app/qml/components/MMProjectItem.qml new file mode 100644 index 000000000..126394590 --- /dev/null +++ b/app/qml/components/MMProjectItem.qml @@ -0,0 +1,257 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick +import QtQuick.Controls + +Rectangle { + id: root + + required property string projectDisplayName + required property string projectId + required property string projectDescription + required property int projectStatus + required property bool projectIsValid + required property bool projectIsLocal + required property bool projectIsMergin + property bool projectIsPending: false + property real projectSyncProgress: 0.0 + property bool highlight: false + + signal openRequested() + signal syncRequested() + signal migrateRequested() + signal removeRequested() + signal stopSyncRequested() + signal showChangesRequested() + + color: root.highlight ? __style.forestColor : __style.whiteColor + radius: 12 * __dp + height: mainColumn.height + + MouseArea { + anchors.fill: parent + enabled: root.projectIsValid + onClicked: openRequested() + } + + Column { + id: mainColumn + + width: parent.width + padding: 20 * __dp + spacing: 12 * __dp + + Row { + id: row + + spacing: 6 * __dp + + Column { + id: column + + spacing: 6 * __dp + + Text { + width: mainColumn.width - 2 * mainColumn.padding - icon.width - row.spacing + + text: root.projectDisplayName + font: __style.t3 + color: root.highlight ? __style.whiteColor : __style.nightColor + elide: Text.ElideRight + opacity: root.projectIsValid ? 1.0 : 0.4 + } + + Row { + width: mainColumn.width - 2 * mainColumn.padding - icon.width - row.spacing + spacing: 4 * __dp + + MMIcon { + id: errorIcon + + source: visible ? __style.errorIcon : "" + color: __style.negativeColor + visible: !root.projectIsValid + } + + Text { + width: parent.width - errorIcon.width + + text: root.projectDescription + font: __style.p6 + elide: Text.ElideRight + color: { + if(root.projectIsValid) { + if(root.highlight) { + return __style.whiteColor + } + return __style.nightColor + } + return __style.grapeColor + } + } + } + } + + Image { + id: icon + + height: column.height + width: height + + source: __style.projectButtonMoreIcon + fillMode: Image.PreserveAspectFit + + MouseArea { + anchors.fill: parent + onClicked: { + root.fillMoreMenu() + listDrawer.visible = true + } + } + } + } + + Item { + height: root.projectIsPending ? syncColumn.height : 0 + width: parent.width + clip: true + + Behavior on height { + NumberAnimation { duration: 250 } + } + + Column { + id: syncColumn + + spacing: mainColumn.spacing + + MMProgressBar { + position: root.projectSyncProgress + width: mainColumn.width - 2 * mainColumn.padding + } + + Row { + id: syncRow + + spacing: 6 * __dp + + Text { + width: mainColumn.width - 2 * mainColumn.padding - stopIcon.width - syncRow.spacing * 2 - stopText.width + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Synchronising...") + font: __style.p6 + color: root.highlight ? __style.whiteColor : __style.nightColor + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + MMIcon { + id: stopIcon + + anchors.verticalCenter: parent.verticalCenter + + source: __style.stopIcon + color: root.highlight ? __style.whiteColor : __style.forestColor + + MouseArea { + anchors.fill: parent + onClicked: root.stopSyncRequested() + } + } + + Text { + id: stopText + + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Stop") + font: __style.t4 + color: root.highlight ? __style.whiteColor : __style.nightColor + verticalAlignment: Text.AlignVCenter + + MouseArea { + anchors.fill: parent + onClicked: root.stopSyncRequested() + } + } + } + } + } + } + + MMListDrawer { + id: listDrawer + + title: qsTr("More options") + model: ListModel { + id: menuModel + } + + onClicked: function(type) { + console.log(type) + switch(type) { + case "download": root.syncRequested(); break + case "sync": root.syncRequested(); break + case "changes": root.showChangesRequested(); break + case "remove": root.removeRequested(); break + case "upload": root.migrateRequested(); break + } + } + } + + function getMoreMenuItems() { + if ( projectIsMergin && projectIsLocal ) + { + if ( ( projectStatus === 2 /*ProjectStatus.NeedsSync*/ ) ) { // uncomment when using this component + return "sync,changes,remove" + } + return "changes,remove" + } + else if ( !projectIsMergin && projectIsLocal ) { + return "upload,remove" + } + return "download" + } + + function fillMoreMenu() { + // fill more menu with corresponding items + let itemsMap = { + "download": { + "name": qsTr("Download from Mergin"), + "iconSource": __style.downloadIcon + }, + "sync": { + "name": qsTr("Synchronize project"), + "iconSource": __style.syncIcon + }, + "changes": { + "name": qsTr("Local changes"), + "iconSource": __style.infoIcon + }, + "remove": { + "name": qsTr("Remove from device"), + "iconSource": __style.deleteIcon + }, + "upload": { + "name": qsTr("Upload to Mergin"), + "iconSource": __style.uploadIcon + } + } + + let items = getMoreMenuItems().split(',') + menuModel.clear() + items.forEach( function(item) { + var json = itemsMap[item] + json.type = item + menuModel.append( json ) + } ) + } +} diff --git a/gallery/qml.qrc b/gallery/qml.qrc index 3746020d5..8127f6ea2 100644 --- a/gallery/qml.qrc +++ b/gallery/qml.qrc @@ -37,6 +37,10 @@ ../app/qml/components/MMMenuDrawer.qml ../app/qml/components/MMToolbarMenuButton.qml ../app/qml/components/MMToolbarLongButton.qml + qml/pages/ProjectItemsPage.qml + ../app/qml/components/MMListDrawer.qml + ../app/qml/components/MMListDrawerItem.qml + ../app/qml/components/MMProjectItem.qml ../app/qml/components/MMMorePhoto.qml ../app/qml/components/MMPhoto.qml ../app/qml/components/MMPhotoGallery.qml diff --git a/gallery/qml/Main.qml b/gallery/qml/Main.qml index 4819ecfb1..2bf7d479c 100644 --- a/gallery/qml/Main.qml +++ b/gallery/qml/Main.qml @@ -148,6 +148,10 @@ ApplicationWindow { title: "Toolbars" source: "ToolbarPage.qml" } + ListElement { + title: "Project items" + source: "ProjectItemsPage.qml" + } } ScrollIndicator.vertical: ScrollIndicator {} diff --git a/gallery/qml/pages/ProjectItemsPage.qml b/gallery/qml/pages/ProjectItemsPage.qml new file mode 100644 index 000000000..f7fa27708 --- /dev/null +++ b/gallery/qml/pages/ProjectItemsPage.qml @@ -0,0 +1,107 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick +import QtQuick.Controls +import "../../app/qml/components" + +Page { + id: pane + + Column { + width: parent.width + anchors.top: parent.top + anchors.topMargin: 20 + anchors.left: parent.left + anchors.leftMargin: 20 + spacing: 20 + + MMProjectItem { + width: 350 + + highlight: true + projectId: "1" + projectStatus: 2 + projectDisplayName: "Mergin local project" + projectDescription: "Highlighted" + projectIsValid: true + projectIsLocal: true + projectIsMergin: true + projectIsPending: true + + onOpenRequested: console.log("onOpenRequested") + onStopSyncRequested: projectIsPending = false + onShowChangesRequested: console.log("onShowChangesRequested") + onSyncRequested: projectIsPending = true + onRemoveRequested: console.log("onRemoveRequested") + onMigrateRequested: console.log("onMigrateRequested") + } + + MMProjectItem { + width: 350 + + highlight: false + projectId: "1" + projectStatus: 2 + projectDisplayName: "Mergin local project" + projectDescription: "Highlighted" + projectIsValid: true + projectIsLocal: true + projectIsMergin: true + + onOpenRequested: console.log("onOpenRequested") + onStopSyncRequested: projectIsPending = false + onShowChangesRequested: console.log("onShowChangesRequested") + onSyncRequested: projectIsPending = true + onRemoveRequested: console.log("onRemoveRequested") + onMigrateRequested: console.log("onMigrateRequested") + } + + MMProjectItem { + width: 350 + + highlight: false + projectId: "1" + projectStatus: 2 + projectDisplayName: "Mergin local project Long Long Long Long Long Long Long" + projectDescription: "Description Description Description Description Description" + projectIsValid: true + projectIsLocal: true + projectIsMergin: true + + onOpenRequested: console.log("onOpenRequested") + onStopSyncRequested: projectIsPending = false + onShowChangesRequested: console.log("onShowChangesRequested") + onSyncRequested: projectIsPending = true + onRemoveRequested: console.log("onRemoveRequested") + onMigrateRequested: console.log("onMigrateRequested") + } + + MMProjectItem { + width: 350 + + highlight: false + projectId: "2" + projectStatus: 0 + projectDisplayName: "Invalid project" + projectDescription: "A project error. A project error. A project error." + projectIsValid: false + projectIsLocal: false + projectIsMergin: false + + onOpenRequested: console.log("onOpenRequested") + onStopSyncRequested: console.log("onStopSyncRequested") + onShowChangesRequested: console.log("onShowChangesRequested") + onSyncRequested: { console.log("onSyncRequested"); projectIsPending = true } + onRemoveRequested: console.log("onRemoveRequested") + onMigrateRequested: console.log("onMigrateRequested") + } + + } +}