diff --git a/INSTALL.md b/INSTALL.md index 25ab5c042..b6b475112 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,7 +17,7 @@ Building Mergin Maps mobile app from source - step by step * [5. Building iOS](#5-building-ios) * [6. Building macOS](#6-building-macos) * [7. Building Windows](#7-building-windows) -* [8. Auto Testing](#9-auto-testing) +* [8. Auto Testing](#8-auto-testing) # 1. Introduction @@ -89,8 +89,7 @@ openssl aes-256-cbc -d -in merginsecrets.cpp.enc -out merginsecrets.cpp -md md5 ## 2.2 Code formatting We use `astyle` to format CPP and Objective-C files. Format is similar to what QGIS has. - -For QML files we use latest version of `jesperhh/qmlfmt`. To install, follow instruction on the GitHub of the project. +For more details about code conventions, please read [our code conventions doc](./docs/code_convention.md). We also use software [pre-commit](https://pre-commit.com/) to automatically check format when doing a commit. You need to install it via `brew`/`pip`, see [installation details](https://pre-commit.com/#installation). @@ -397,7 +396,7 @@ or you need some user with unlimited projects limit. First workspace from list i now you need to set environment variables: ``` -TEST_MERGIN_URL=test.dev.merginmaps.com +TEST_MERGIN_URL=https://test.dev.merginmaps.com/ TEST_API_USERNAME=test_mobileapp_dev TEST_API_PASSWORD= ``` diff --git a/README.md b/README.md index 66c2a30eb..d703f00b5 100644 --- a/README.md +++ b/README.md @@ -82,5 +82,9 @@ To setup your development environment, read [INSTALL](./INSTALL.md) New sub-project 'gallery' app is used to develop/design all UI components, used in the Mergin Maps app +### Code conventions + +To learn about our code conventions, please see the [code conventions](./docs/code_convention.md) file. + ## Privacy policy Read more about the app privacy policy [here](https://merginmaps.com/docs/reference/privacy/) diff --git a/app/attributes/attributecontroller.cpp b/app/attributes/attributecontroller.cpp index 4f7ee98cb..fa5c4c2b7 100644 --- a/app/attributes/attributecontroller.cpp +++ b/app/attributes/attributecontroller.cpp @@ -1056,15 +1056,13 @@ bool AttributeController::deleteFeature() if ( !mFeatureLayerPair.layer() ) return false; - bool rv = true; - if ( !startEditing() ) { - rv = false; + return false; } bool isDeleted = mFeatureLayerPair.layer()->deleteFeature( mFeatureLayerPair.feature().id() ); - rv = commit(); + bool rv = commit(); if ( !isDeleted || !rv ) { diff --git a/app/icons/AcceptInvitationImage.svg b/app/icons/AcceptInvitationImage.svg new file mode 100644 index 000000000..cf7101ff4 --- /dev/null +++ b/app/icons/AcceptInvitationImage.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/AcceptInvitationLogoImage.svg b/app/icons/AcceptInvitationLogoImage.svg new file mode 100644 index 000000000..8075b2f04 --- /dev/null +++ b/app/icons/AcceptInvitationLogoImage.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/icons/Archaeology.svg b/app/icons/Archaeology.svg new file mode 100644 index 000000000..b6e36d66a --- /dev/null +++ b/app/icons/Archaeology.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Arrow Down.svg b/app/icons/ArrowDown.svg similarity index 100% rename from app/icons/Arrow Down.svg rename to app/icons/ArrowDown.svg diff --git a/app/icons/Arrow Link Right.svg b/app/icons/ArrowLinkRight.svg similarity index 100% rename from app/icons/Arrow Link Right.svg rename to app/icons/ArrowLinkRight.svg diff --git a/app/icons/Arrow Up.svg b/app/icons/ArrowUp.svg similarity index 100% rename from app/icons/Arrow Up.svg rename to app/icons/ArrowUp.svg diff --git a/app/icons/Back.svg b/app/icons/Back.svg new file mode 100644 index 000000000..f7180a4ee --- /dev/null +++ b/app/icons/Back.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Briefcase.svg b/app/icons/Briefcase.svg new file mode 100644 index 000000000..923bcb6d3 --- /dev/null +++ b/app/icons/Briefcase.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Bubble.svg b/app/icons/Bubble.svg new file mode 100644 index 000000000..0912079af --- /dev/null +++ b/app/icons/Bubble.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/Electricity.svg b/app/icons/Electricity.svg new file mode 100644 index 000000000..43790e6ba --- /dev/null +++ b/app/icons/Electricity.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Engineering.svg b/app/icons/Engineering.svg new file mode 100644 index 000000000..a7ebc6700 --- /dev/null +++ b/app/icons/Engineering.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Environmental.svg b/app/icons/Environmental.svg new file mode 100644 index 000000000..6d9b067d9 --- /dev/null +++ b/app/icons/Environmental.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Facebook.svg b/app/icons/Facebook.svg new file mode 100644 index 000000000..38867f50a --- /dev/null +++ b/app/icons/Facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Globe.svg b/app/icons/Globe.svg new file mode 100644 index 000000000..f203aab54 --- /dev/null +++ b/app/icons/Globe.svg @@ -0,0 +1,7 @@ + + + + + + + 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/Linkedin.svg b/app/icons/Linkedin.svg new file mode 100644 index 000000000..4165bb5c2 --- /dev/null +++ b/app/icons/Linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Mastodon.svg b/app/icons/Mastodon.svg new file mode 100644 index 000000000..d072c2dd4 --- /dev/null +++ b/app/icons/Mastodon.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/MorePhotos.svg b/app/icons/MorePhotos.svg new file mode 100644 index 000000000..1a31905b3 --- /dev/null +++ b/app/icons/MorePhotos.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Mouth.svg b/app/icons/Mouth.svg new file mode 100644 index 000000000..ad2a5efca --- /dev/null +++ b/app/icons/Mouth.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Natural Resources.svg b/app/icons/Natural Resources.svg new file mode 100644 index 000000000..612c85e11 --- /dev/null +++ b/app/icons/Natural Resources.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Other.svg b/app/icons/Other.svg new file mode 100644 index 000000000..930aa1085 --- /dev/null +++ b/app/icons/Other.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Others.svg b/app/icons/Others.svg new file mode 100644 index 000000000..d0eb5a4f4 --- /dev/null +++ b/app/icons/Others.svg @@ -0,0 +1,7 @@ + + + + + + + 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/QGIS.svg b/app/icons/QGIS.svg new file mode 100644 index 000000000..25efb61f8 --- /dev/null +++ b/app/icons/QGIS.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/QR Code.svg b/app/icons/QRCode.svg similarity index 100% rename from app/icons/QR Code.svg rename to app/icons/QRCode.svg diff --git a/app/icons/Reddit.svg b/app/icons/Reddit.svg new file mode 100644 index 000000000..fe4c99b32 --- /dev/null +++ b/app/icons/Reddit.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/SocialMedia.svg b/app/icons/SocialMedia.svg new file mode 100644 index 000000000..158775f13 --- /dev/null +++ b/app/icons/SocialMedia.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/StateAndLocal.svg b/app/icons/StateAndLocal.svg new file mode 100644 index 000000000..e734c79ed --- /dev/null +++ b/app/icons/StateAndLocal.svg @@ -0,0 +1,7 @@ + + + + + + + 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/Subscriptions.svg b/app/icons/Subscriptions.svg new file mode 100644 index 000000000..70b5368c2 --- /dev/null +++ b/app/icons/Subscriptions.svg @@ -0,0 +1,6 @@ + + + + + + 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/Teacher.svg b/app/icons/Teacher.svg new file mode 100644 index 000000000..252d67a86 --- /dev/null +++ b/app/icons/Teacher.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Telecommunication.svg b/app/icons/Telecommunication.svg new file mode 100644 index 000000000..1b043a67c --- /dev/null +++ b/app/icons/Telecommunication.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Terms.svg b/app/icons/Terms.svg new file mode 100644 index 000000000..b358dd1bf --- /dev/null +++ b/app/icons/Terms.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Tractor.svg b/app/icons/Tractor.svg new file mode 100644 index 000000000..acc009e88 --- /dev/null +++ b/app/icons/Tractor.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Transportation.svg b/app/icons/Transportation.svg new file mode 100644 index 000000000..cf5779377 --- /dev/null +++ b/app/icons/Transportation.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/WaterResources.svg b/app/icons/WaterResources.svg new file mode 100644 index 000000000..50ce29ca5 --- /dev/null +++ b/app/icons/WaterResources.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/X Mark.svg b/app/icons/XMark.svg similarity index 100% rename from app/icons/X Mark.svg rename to app/icons/XMark.svg diff --git a/app/icons/XTwitter.svg b/app/icons/XTwitter.svg new file mode 100644 index 000000000..82a91bec7 --- /dev/null +++ b/app/icons/XTwitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/Youtube.svg b/app/icons/Youtube.svg new file mode 100644 index 000000000..ccd699107 --- /dev/null +++ b/app/icons/Youtube.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/icons/icons.qrc b/app/icons/icons.qrc index 832b11c2e..2f9b3b4bb 100644 --- a/app/icons/icons.qrc +++ b/app/icons/icons.qrc @@ -1,24 +1,60 @@ - Arrow Link Right.svg + AcceptInvitationImage.svg + AcceptInvitationLogoImage.svg + Archaeology.svg + ArrowDown.svg + ArrowLinkRight.svg + ArrowUp.svg + Back.svg + Briefcase.svg + Bubble.svg Calendar.svg - Hide.svg - Search.svg - Show.svg - X Mark.svg - Error.svg - Arrow Down.svg - Arrow Up.svg - QR Code.svg Checkmark.svg - CloseButton.svg - UploadImage.svg - ReachedDataLimitImage.svg Close.svg - Waiting.svg + CloseButton.svg Delete.svg Done.svg + Download.svg Edit.svg + Electricity.svg + Engineering.svg + Environmental.svg + Error.svg + Facebook.svg + Hide.svg + Info.svg + Linkedin.svg + Mastodon.svg More.svg + MorePhotos.svg + Mouth.svg + Natural Resources.svg + Other.svg + Others.svg + ProjectButtonMore.svg + QGIS.svg + QRCode.svg + ReachedDataLimitImage.svg + Reddit.svg + Search.svg + Show.svg + SocialMedia.svg + StateAndLocal.svg + Stop.svg + Subscriptions.svg + Sync.svg + Teacher.svg + Telecommunication.svg + Terms.svg + Tractor.svg + Transportation.svg + UploadImage.svg + Waiting.svg + WaterResources.svg + XMark.svg + XTwitter.svg + Youtube.svg + Globe.svg diff --git a/app/mmstyle.h b/app/mmstyle.h index 781dfd00d..26f75f05a 100644 --- a/app/mmstyle.h +++ b/app/mmstyle.h @@ -23,6 +23,11 @@ class MMStyle: public QObject // standard - font: __style.p5 // extended - font.pixelSize: __style.p5.pixelSize; font.italic: true + // Fonts - Heading + Q_PROPERTY( QFont h1 READ h1 CONSTANT ) + Q_PROPERTY( QFont h2 READ h2 CONSTANT ) + Q_PROPERTY( QFont h3 READ h3 CONSTANT ) + // Fonts - Title Q_PROPERTY( QFont t1 READ t1 CONSTANT ) Q_PROPERTY( QFont t2 READ t2 CONSTANT ) @@ -75,28 +80,64 @@ class MMStyle: public QObject Q_PROPERTY( QColor shadowColor READ shadowColor CONSTANT ) // Icons + Q_PROPERTY( QUrl archaeologyIcon READ archaeologyIcon CONSTANT ) + Q_PROPERTY( QUrl arrowDownIcon READ arrowDownIcon CONSTANT ) Q_PROPERTY( QUrl arrowLinkRightIcon READ arrowLinkRightIcon CONSTANT ) - Q_PROPERTY( QUrl searchIcon READ searchIcon CONSTANT ) - Q_PROPERTY( QUrl calendarIcon READ calendarIcon CONSTANT ) - Q_PROPERTY( QUrl showIcon READ showIcon CONSTANT ) - Q_PROPERTY( QUrl hideIcon READ hideIcon CONSTANT ) - Q_PROPERTY( QUrl xMarkIcon READ xMarkIcon CONSTANT ) - Q_PROPERTY( QUrl errorIcon READ errorIcon CONSTANT ) Q_PROPERTY( QUrl arrowUpIcon READ arrowUpIcon CONSTANT ) - Q_PROPERTY( QUrl arrowDownIcon READ arrowDownIcon CONSTANT ) - Q_PROPERTY( QUrl qrCodeIcon READ qrCodeIcon CONSTANT ) + Q_PROPERTY( QUrl backIcon READ backIcon CONSTANT ) + Q_PROPERTY( QUrl briefcaseIcon READ briefcaseIcon CONSTANT ) + Q_PROPERTY( QUrl bubbleIcon READ bubbleIcon CONSTANT ) + Q_PROPERTY( QUrl calendarIcon READ calendarIcon CONSTANT ) Q_PROPERTY( QUrl checkmarkIcon READ checkmarkIcon CONSTANT ) Q_PROPERTY( QUrl closeButtonIcon READ closeButtonIcon CONSTANT ) Q_PROPERTY( QUrl closeIcon READ closeIcon CONSTANT ) - Q_PROPERTY( QUrl waitingIcon READ waitingIcon CONSTANT ) Q_PROPERTY( QUrl deleteIcon READ deleteIcon CONSTANT ) Q_PROPERTY( QUrl doneIcon READ doneIcon CONSTANT ) + Q_PROPERTY( QUrl downloadIcon READ downloadIcon CONSTANT ) Q_PROPERTY( QUrl editIcon READ editIcon CONSTANT ) + Q_PROPERTY( QUrl electricityIcon READ electricityIcon CONSTANT ) + Q_PROPERTY( QUrl engineeringIcon READ engineeringIcon CONSTANT ) + Q_PROPERTY( QUrl environmentalIcon READ environmentalIcon CONSTANT ) + Q_PROPERTY( QUrl errorIcon READ errorIcon CONSTANT ) + Q_PROPERTY( QUrl facebookIcon READ facebookIcon CONSTANT ) + Q_PROPERTY( QUrl hideIcon READ hideIcon CONSTANT ) + Q_PROPERTY( QUrl infoIcon READ infoIcon CONSTANT ) + Q_PROPERTY( QUrl linkedinIcon READ linkedinIcon CONSTANT ) + Q_PROPERTY( QUrl mastodonIcon READ mastodonIcon CONSTANT ) Q_PROPERTY( QUrl moreIcon READ moreIcon CONSTANT ) + Q_PROPERTY( QUrl morePhotosIcon READ morePhotosIcon CONSTANT ) + Q_PROPERTY( QUrl mouthIcon READ mouthIcon CONSTANT ) + Q_PROPERTY( QUrl naturalResourcesIcon READ naturalResourcesIcon CONSTANT ) + Q_PROPERTY( QUrl otherIcon READ otherIcon CONSTANT ) + Q_PROPERTY( QUrl othersIcon READ othersIcon CONSTANT ) + Q_PROPERTY( QUrl projectButtonMoreIcon READ projectButtonMoreIcon CONSTANT ) + Q_PROPERTY( QUrl qgisIcon READ qgisIcon CONSTANT ) + Q_PROPERTY( QUrl qrCodeIcon READ qrCodeIcon CONSTANT ) + Q_PROPERTY( QUrl redditIcon READ redditIcon CONSTANT ) + Q_PROPERTY( QUrl searchIcon READ searchIcon CONSTANT ) + Q_PROPERTY( QUrl showIcon READ showIcon CONSTANT ) + Q_PROPERTY( QUrl socialMediaIcon READ socialMediaIcon CONSTANT ) + Q_PROPERTY( QUrl stateAndLocalIcon READ stateAndLocalIcon CONSTANT ) + Q_PROPERTY( QUrl stopIcon READ stopIcon CONSTANT ) + Q_PROPERTY( QUrl subscriptionsIcon READ subscriptionsIcon CONSTANT ) + Q_PROPERTY( QUrl syncIcon READ syncIcon CONSTANT ) + Q_PROPERTY( QUrl teacherIcon READ teacherIcon CONSTANT ) + Q_PROPERTY( QUrl telecommunicationIcon READ telecommunicationIcon CONSTANT ) + Q_PROPERTY( QUrl termsIcon READ termsIcon CONSTANT ) + Q_PROPERTY( QUrl tractorIcon READ tractorIcon CONSTANT ) + Q_PROPERTY( QUrl transportationIcon READ transportationIcon CONSTANT ) + Q_PROPERTY( QUrl waitingIcon READ waitingIcon CONSTANT ) + Q_PROPERTY( QUrl waterResourcesIcon READ waterResourcesIcon CONSTANT ) + Q_PROPERTY( QUrl xMarkIcon READ xMarkIcon CONSTANT ) + Q_PROPERTY( QUrl xTwitterIcon READ xTwitterIcon CONSTANT ) + Q_PROPERTY( QUrl youtubeIcon READ youtubeIcon CONSTANT ) + Q_PROPERTY( QUrl globeIcon READ globeIcon CONSTANT ) // Images - Q_PROPERTY( QUrl uploadImage READ uploadImage CONSTANT ) + Q_PROPERTY( QUrl acceptInvitationImage READ acceptInvitationImage CONSTANT ) + Q_PROPERTY( QUrl acceptInvitationLogoImage READ acceptInvitationLogoImage CONSTANT ) Q_PROPERTY( QUrl reachedDataLimitImage READ reachedDataLimitImage CONSTANT ) + Q_PROPERTY( QUrl uploadImage READ uploadImage CONSTANT ) // Map items Q_PROPERTY( double mapItemHeight READ mapItemHeight CONSTANT ) @@ -105,16 +146,24 @@ class MMStyle: public QObject Q_PROPERTY( double toolbarHeight READ toolbarHeight CONSTANT ) Q_PROPERTY( double menuDrawerHeight READ menuDrawerHeight CONSTANT ) + // Other + Q_PROPERTY( double inputRadius READ inputRadius CONSTANT ) + Q_PROPERTY( double maxPageWidth READ maxPageWidth CONSTANT ) + public: explicit MMStyle( qreal dp ) : mDp( dp ) {} ~MMStyle() = default; + QFont h1() {return fontFactory( 48, true );} + QFont h2() {return fontFactory( 36, true );} + QFont h3() {return fontFactory( 24, true );} + QFont t1() {return fontFactory( 18, true );} QFont t2() {return fontFactory( 16, true );} QFont t3() {return fontFactory( 14, true );} - QFont t4() {return fontFactory( 18, true );} + QFont t4() {return fontFactory( 12, true );} QFont t5() {return fontFactory( 10, true );} QFont p1() {return fontFactory( 32, false );} @@ -155,25 +204,61 @@ class MMStyle: public QObject QColor errorBgInputColor() {return QColor::fromString( "#FEFAF9" );} QColor shadowColor() {return QColor::fromString( "#66777777" );} - QUrl arrowLinkRightIcon() {return QUrl( "qrc:/Arrow Link Right.svg" );} - QUrl searchIcon() {return QUrl( "qrc:/Search.svg" );} + QUrl archaeologyIcon() {return QUrl( "qrc:/Archaeology.svg" );} + QUrl arrowDownIcon() {return QUrl( "qrc:/ArrowDown.svg" );} + QUrl arrowLinkRightIcon() {return QUrl( "qrc:/ArrowLinkRight.svg" );} + QUrl arrowUpIcon() {return QUrl( "qrc:/ArrowUp.svg" );} + QUrl backIcon() {return QUrl( "qrc:/Back.svg" );} + QUrl briefcaseIcon() {return QUrl( "qrc:/Briefcase.svg" );} + QUrl bubbleIcon() {return QUrl( "qrc:/Bubble.svg" );} QUrl calendarIcon() {return QUrl( "qrc:/Calendar.svg" );} - QUrl showIcon() {return QUrl( "qrc:/Show.svg" );} - QUrl hideIcon() {return QUrl( "qrc:/Hide.svg" );} - QUrl xMarkIcon() {return QUrl( "qrc:/X Mark.svg" );} - QUrl errorIcon() {return QUrl( "qrc:/Error.svg" );} - QUrl arrowUpIcon() {return QUrl( "qrc:/Arrow Up.svg" );} - QUrl arrowDownIcon() {return QUrl( "qrc:/Arrow Down.svg" );} - QUrl qrCodeIcon() {return QUrl( "qrc:/QR Code.svg" );} QUrl checkmarkIcon() {return QUrl( "qrc:/Checkmark.svg" );} QUrl closeButtonIcon() {return QUrl( "qrc:/CloseButton.svg" );} QUrl closeIcon() {return QUrl( "qrc:/Close.svg" );} - QUrl waitingIcon() {return QUrl( "qrc:/Waiting.svg" );} QUrl deleteIcon() {return QUrl( "qrc:/Delete.svg" );} QUrl doneIcon() {return QUrl( "qrc:/Done.svg" );} + QUrl downloadIcon() {return QUrl( "qrc:/Download.svg" );} QUrl editIcon() {return QUrl( "qrc:/Edit.svg" );} + QUrl electricityIcon() {return QUrl( "qrc:/Electricity.svg" );} + QUrl engineeringIcon() {return QUrl( "qrc:/Engineering.svg" );} + QUrl environmentalIcon() {return QUrl( "qrc:/Environmental.svg" );} + QUrl errorIcon() {return QUrl( "qrc:/Error.svg" );} + QUrl facebookIcon() {return QUrl( "qrc:/Facebook.svg" );} + QUrl globeIcon() {return QUrl( "qrc:/Globe.svg" );} + QUrl hideIcon() {return QUrl( "qrc:/Hide.svg" );} + QUrl infoIcon() {return QUrl( "qrc:/Info.svg" );} + QUrl linkedinIcon() {return QUrl( "qrc:/Linkedin.svg" );} + QUrl mastodonIcon() {return QUrl( "qrc:/Mastodon.svg" );} QUrl moreIcon() {return QUrl( "qrc:/More.svg" );} + QUrl morePhotosIcon() {return QUrl( "qrc:/MorePhotos.svg" );} + QUrl mouthIcon() {return QUrl( "qrc:/Mouth.svg" );} + QUrl naturalResourcesIcon() {return QUrl( "qrc:/Natural Resources.svg" );} + QUrl otherIcon() {return QUrl( "qrc:/Other.svg" );} + QUrl othersIcon() {return QUrl( "qrc:/Others.svg" );} + QUrl projectButtonMoreIcon() {return QUrl( "qrc:/ProjectButtonMore.svg" );} + QUrl qgisIcon() {return QUrl( "qrc:/QGIS.svg" );} + QUrl qrCodeIcon() {return QUrl( "qrc:/QRCode.svg" );} + QUrl redditIcon() {return QUrl( "qrc:/Reddit.svg" );} + QUrl searchIcon() {return QUrl( "qrc:/Search.svg" );} + QUrl showIcon() {return QUrl( "qrc:/Show.svg" );} + QUrl socialMediaIcon() {return QUrl( "qrc:/SocialMedia.svg" );} + QUrl stateAndLocalIcon() {return QUrl( "qrc:/StateAndLocal.svg" );} + QUrl stopIcon() {return QUrl( "qrc:/Stop.svg" );} + QUrl subscriptionsIcon() {return QUrl( "qrc:/Subscriptions.svg" );} + QUrl syncIcon() {return QUrl( "qrc:/Sync.svg" );} + QUrl teacherIcon() {return QUrl( "qrc:/Teacher.svg" );} + QUrl telecommunicationIcon() {return QUrl( "qrc:/Telecommunication.svg" );} + QUrl termsIcon() {return QUrl( "qrc:/Terms.svg" );} + QUrl tractorIcon() {return QUrl( "qrc:/Tractor.svg" );} + QUrl transportationIcon() {return QUrl( "qrc:/Transportation.svg" );} + QUrl waitingIcon() {return QUrl( "qrc:/Waiting.svg" );} + QUrl waterResourcesIcon() {return QUrl( "qrc:/WaterResources.svg" );} + QUrl xMarkIcon() {return QUrl( "qrc:/XMark.svg" );} + QUrl xTwitterIcon() {return QUrl( "qrc:/XTwitter.svg" );} + QUrl youtubeIcon() {return QUrl( "qrc:/Youtube.svg" );} + QUrl acceptInvitationLogoImage() {return QUrl( "qrc:/AcceptInvitationLogoImage.svg" ); } + QUrl acceptInvitationImage() {return QUrl( "qrc:/AcceptInvitationImage.svg" ); } QUrl uploadImage() {return QUrl( "qrc:/UploadImage.svg" );} QUrl reachedDataLimitImage() {return QUrl( "qrc:/ReachedDataLimitImage.svg" );} @@ -182,6 +267,9 @@ class MMStyle: public QObject double toolbarHeight() {return 89 * mDp;} double menuDrawerHeight() {return 67 * mDp;} + double inputRadius() {return 12 * mDp;} + double maxPageWidth() {return 500 * mDp;} + signals: void styleChanged(); diff --git a/app/projectsmodel.cpp b/app/projectsmodel.cpp index c53c18caf..b7a36b9b4 100644 --- a/app/projectsmodel.cpp +++ b/app/projectsmodel.cpp @@ -548,7 +548,7 @@ void ProjectsModel::onAuthChanged() { if ( !mBackend->userAuth() || !mBackend->userAuth()->hasAuthData() ) // user logged out, clear created and shared lists { - if ( mModelType == CreatedProjectsModel || mModelType == SharedProjectsModel ) + if ( mModelType == WorkspaceProjectsModel ) { clearProjects(); } @@ -586,10 +586,6 @@ QString ProjectsModel::modelTypeToFlag() const { switch ( mModelType ) { - case CreatedProjectsModel: - return QStringLiteral( "created" ); - case SharedProjectsModel: - return QStringLiteral( "shared" ); case WorkspaceProjectsModel: return QStringLiteral( "workspace" ); case PublicProjectsModel: diff --git a/app/projectsmodel.h b/app/projectsmodel.h index 27cba6f6c..88ea48a4a 100644 --- a/app/projectsmodel.h +++ b/app/projectsmodel.h @@ -29,7 +29,7 @@ class LocalProjectsManager; * * Model can have different types that affect handling of the projects. * - LocalProjectsModel always keeps all local projects and seek their mergin part when listProjectsByNameFinished - * - Created-, Shared-, and PublicProjectsModel does the opposite, keeps all mergin projects and seeks their local part in projects from LocalProjectsManager + * - Workspace-, and PublicProjectsModel does the opposite, keeps all mergin projects and seeks their local part in projects from LocalProjectsManager * - EmptyProjectsModel is default state * * To avoid overriding of requests, model remembers last sent request ID and upon receiving signal from MerginAPI about listProjectsFinished, it firsts compares @@ -68,15 +68,13 @@ class ProjectsModel : public QAbstractListModel /** * \brief The ProjectModelTypes enum: * - LocalProjectsModel always keeps all local projects and seek their mergin part when listProjectsByNameFinished - * - Created-, Shared-, and PublicProjectsModel does the opposite, keeps all mergin projects and seeks their local part in projects from LocalProjectsManager + * - Workspace-, and PublicProjectsModel does the opposite, keeps all mergin projects and seeks their local part in projects from LocalProjectsManager * - EmptyProjectsModel is default state */ enum ProjectModelTypes { EmptyProjectsModel = 0, // default, holding no projects ~ invalid model LocalProjectsModel, - CreatedProjectsModel, - SharedProjectsModel, PublicProjectsModel, WorkspaceProjectsModel, RecentProjectsModel diff --git a/app/qml/AccountPage.qml b/app/qml/AccountPage.qml deleted file mode 100644 index 23c8b98e8..000000000 --- a/app/qml/AccountPage.qml +++ /dev/null @@ -1,394 +0,0 @@ -/*************************************************************************** - * * - * 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 Qt5Compat.GraphicalEffects -import QtQuick.Layouts -import QtQuick.Dialogs -import lc 1.0 -import "." // import InputStyle singleton -import "./components" - -Page { - signal back - signal managePlansClicked - signal signOutClicked - signal accountDeleted - property color bgColor: "white" - property real fieldHeight: InputStyle.rowHeight - - property string username: __merginApi.userAuth.username - property string email: __merginApi.userInfo.email - property int diskUsage: __merginApi.workspaceInfo.diskUsage - property int storageLimit: __merginApi.workspaceInfo.storageLimit - property string planAlias: __merginApi.subscriptionInfo.planAlias - property int subscriptionStatus: __merginApi.subscriptionInfo.subscriptionStatus - property string subscriptionsTimestamp: __merginApi.subscriptionInfo.subscriptionTimestamp - property string nextBillPrice: __merginApi.subscriptionInfo.nextBillPrice - property bool ownsActiveSubscription: __merginApi.subscriptionInfo.ownsActiveSubscription - property bool apiSupportsSubscriptions: __merginApi.apiSupportsSubscriptions - - id: root - visible: true - - - // ///////////////// - // header - PanelHeader { - id: header - height: InputStyle.rowHeightHeader - width: parent.width - color: InputStyle.clrPanelMain - rowHeight: InputStyle.rowHeightHeader - titleText: qsTr("My Account") - onBack: root.back() - withBackButton: true - } - - - ScrollView { - id: scrollView - anchors.top: header.bottom - width: root.width - height: root.height - header.height - contentHeight: accountBodyContainer.height + footer.height - clip: true - - // ///////////////// - // Body - Column { - id: accountBodyContainer - width: root.width - height: Math.max(accountBodyContainer.childrenRect.height, scrollView.height - footer.height) - - // avatar - Row { - id: avatarContainer - height: InputStyle.rowHeightHeader * 2 - anchors.horizontalCenter: parent.horizontalCenter - - Item { - id: avatar - width: avatarContainer.height - height: width - - Rectangle { - id: avatarImage - anchors.centerIn: parent - width: avatar.width * 0.8 - height: width - color: InputStyle.fontColor - radius: width*0.5 - antialiasing: true - - Image { - id: userIcon - anchors.centerIn: parent - source: InputStyle.accountIcon - height: parent.height * 0.8 - width: height - sourceSize.width: width - sourceSize.height: height - fillMode: Image.PreserveAspectFit - } - - ColorOverlay { - anchors.fill: userIcon - source: userIcon - color: "#FFFFFF" - } - } - } - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - source: InputStyle.accountIcon - text: root.username - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - source: InputStyle.envelopeIcon - text: root.email - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - visible: root.apiSupportsSubscriptions - source: InputStyle.editIcon - text: root.planAlias - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - visible: root.subscriptionStatus === MerginSubscriptionStatus.SubscriptionUnsubscribed - source: InputStyle.infoIcon - text: qsTr("Your subscription will not auto-renew after %1") - .arg(root.subscriptionsTimestamp) - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - visible: root.subscriptionStatus === MerginSubscriptionStatus.SubscriptionInGracePeriod - source: InputStyle.exclamationTriangleIcon - onLinkActivated: function( link ) { - Qt.openUrlExternally(link) - } - text: qsTr("Please update your %1billing details%2 as soon as possible") - .arg("") - .arg("") - iconColor: InputStyle.highlightColor - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - visible: root.subscriptionStatus === MerginSubscriptionStatus.ValidSubscription - source: InputStyle.todayIcon - text: qsTr("Your next bill will be for %1 on %2") - .arg(root.nextBillPrice) - .arg(root.subscriptionsTimestamp) - } - - TextWithIcon { - width: parent.width - height: InputStyle.rowHeight - visible: root.subscriptionStatus === MerginSubscriptionStatus.CanceledSubscription - source: InputStyle.todayIcon - text: qsTr("Your subscription was cancelled on %1") - .arg(root.subscriptionsTimestamp) - } - - Row { - width: parent.width - Item { - width: root.fieldHeight - height: root.fieldHeight - - CircularProgressBar { - id: storageIcon - width: parent.width*0.6 - anchors.centerIn: parent - height: width - value: root.diskUsage/root.storageLimit - } - } - - Text { - id: textItem - height: root.fieldHeight - verticalAlignment: Text.AlignVCenter - font.pixelSize: InputStyle.fontPixelSizeNormal - color: InputStyle.fontColor - text: qsTr("Using %1 / %2").arg(__inputUtils.bytesToHumanSize(root.diskUsage)).arg(__inputUtils.bytesToHumanSize(root.storageLimit)) - } - } - - Item { - //spacer - height: 10 * InputStyle.dp - width: parent.width - } - - Button { - id: subscribeButton - width: root.width - 2 * InputStyle.rowHeightHeader - anchors.horizontalCenter: parent.horizontalCenter - visible: __merginApi.apiSupportsSubscriptions - - height: InputStyle.rowHeightHeader - text: root.ownsActiveSubscription ? qsTr("Manage Subscription") : qsTr("Subscription plans") - font.pixelSize: InputStyle.fontPixelSizeBig - - background: Rectangle { - color: InputStyle.highlightColor - } - - onClicked: managePlansClicked() - - contentItem: Text { - text: subscribeButton.text - font: subscribeButton.font - opacity: enabled ? 1.0 : 0.3 - color: "white" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - } - - // ///////////////// - // Footer - Item { - id: footer - height: InputStyle.rowHeight * 2 - width: parent.width - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - - Column { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - spacing: 5 - - Button { - id: signOutButton - height: InputStyle.rowHeightHeader - text: qsTr("Sign out") - font.pixelSize: InputStyle.fontPixelSizeNormal - font.bold: true - anchors.horizontalCenter: parent.horizontalCenter - background: Rectangle { - color: root.bgColor - } - - onClicked: signOutClicked() - - contentItem: Text { - text: signOutButton.text - font: signOutButton.font - color: InputStyle.highlightColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - - Button { - id: deleteAccountButton - height: InputStyle.rowHeightHeader - text: qsTr("Delete account") - font.pixelSize: InputStyle.fontPixelSizeNormal - font.bold: true - anchors.horizontalCenter: parent.horizontalCenter - background: Rectangle { - color: root.bgColor - } - - onClicked: accountDeleteDialog.open() - - contentItem: Text { - text: deleteAccountButton.text - font: deleteAccountButton.font - color: InputStyle.invalidButtonColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - - } - } - - Dialog { - id: accountDeleteDialog - visible: false - modal: true - spacing: InputStyle.panelSpacing - anchors.centerIn: parent - leftMargin: InputStyle.panelMargin - rightMargin: InputStyle.panelMargin - title: qsTr( "Delete account?" ) - - contentItem: ColumnLayout { - id: column - Label { - id: label - text: qsTr("This action will delete your Mergin Maps account with all your projects, " + - "both on the device and on the server. This action cannot be undone. " + - "If you have an Apple subscription you need to cancel it manually.\n\n" + - "In order to delete your account, enter your username in the field below and click Yes.") - Layout.fillWidth: true - wrapMode: Text.WordWrap - } - TextField { - id: usernameField - placeholderText: qsTr("Enter username") - Layout.fillWidth: true - onTextEdited: function() { - buttons.standardButton(Dialog.Yes).enabled = (text === username) - } - } - } - - footer: DialogButtonBox { - id: buttons - standardButtons: Dialog.Yes | Dialog.No - } - - onAboutToShow: { - buttons.standardButton(Dialog.Yes).enabled = false; - } - - onAboutToHide: { - usernameField.clear() - } - - onAccepted: { - close() - accountDeleteIndicator.running = true - __merginApi.deleteAccount() - } - onRejected: { - close() - } - } - - MessageDialog { - id: accountDeletionFailedDialog - - visible: false - title: qsTr( "Failed to remove account" ) - text: qsTr( "An error occured while removing your account" ) - buttons: MessageDialog.Close - - onButtonClicked: function( clickedButton ) { - close() - } - } - - BusyIndicator { - id: accountDeleteIndicator - width: root.width/8 - height: width - running: false - visible: running - anchors.centerIn: parent - z: root.z + 1 - } - - Connections { - target: __merginApi - - function onUserIsAnOrgOwnerError() { - accountDeleteIndicator.running = false - accountDeletionFailedDialog.text = qsTr("Can not close account because user is the only owner of an organisation.\n\n" + - "Please go to the Mergin Maps website to remove it manually.") - accountDeletionFailedDialog.open() - } - function onAccountDeleted( result ) { - accountDeleteIndicator.running = false - if ( result ) { - accountDeleted() - } - else { - accountDeletionFailedDialog.open() - } - } - } - } -} diff --git a/app/qml/CMakeLists.txt b/app/qml/CMakeLists.txt index adfdf6ac8..bf394e727 100644 --- a/app/qml/CMakeLists.txt +++ b/app/qml/CMakeLists.txt @@ -89,7 +89,6 @@ set(MM_QML misc/NoWorkspaceBanner.qml misc/PositionProviderPage.qml AboutPanel.qml - AccountPage.qml AuthPanel.qml Banner.qml ChangelogPanel.qml diff --git a/app/qml/LoginForm.qml b/app/qml/LoginForm.qml index fa04ddfec..76fe9b15c 100644 --- a/app/qml/LoginForm.qml +++ b/app/qml/LoginForm.qml @@ -230,9 +230,14 @@ Rectangle { } visible: { + if ( __merginApi.serverType !== MerginServerType.OLD ) { + return false; + } + if ( __merginApi.serverType === MerginServerType.CE ) { return false; } + return true; } } diff --git a/app/qml/ProjectPanel.qml b/app/qml/ProjectPanel.qml index 87146f58f..e6790e26d 100644 --- a/app/qml/ProjectPanel.qml +++ b/app/qml/ProjectPanel.qml @@ -127,7 +127,7 @@ Item { StackView { id: stackView - initialItem: __merginApi.serverType === MerginServerType.OLD ? projectsPanelComp : workspaceProjectsPanelComp + initialItem: workspaceProjectsPanelComp anchors { top: noWorkspaceBanner.visible ? noWorkspaceBanner.bottom : parent.top @@ -177,17 +177,6 @@ Item { return false; } - function switchUI() { - stackView.clear( StackView.Immediate ) - - if ( __merginApi.serverType === MerginServerType.OLD ) { - stackView.push( projectsPanelComp ) - } - else { - stackView.push( workspaceProjectsPanelComp ) - } - } - Keys.onReleased: function( event ) { if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) { event.accepted = true; @@ -217,363 +206,6 @@ Item { z: parent.z + 1 } - Component { - id: projectsPanelComp - - Page { - id: projectsPage - - function refreshProjectList( keepSearchFilter = false ) { - stackView.pending = true - switch( pageContent.state ) { - case "local": - localProjectsPage.refreshProjectsList( keepSearchFilter ) - break - case "created": - createdProjectsPage.refreshProjectsList( keepSearchFilter ) - break - case "shared": - sharedProjectsPage.refreshProjectsList( keepSearchFilter ) - break - case "public": - publicProjectsPage.refreshProjectsList( keepSearchFilter ) - break - } - } - - header: PanelHeader { - id: pageHeader - - titleText: qsTr("Projects") - color: InputStyle.clrPanelMain - height: InputStyle.rowHeightHeader - rowHeight: InputStyle.rowHeightHeader - - onBack: { - if ( root.activeProjectId ) { - root.hidePanel() - } - } - withBackButton: root.activeProjectId - - Item { - id: avatar - - width: InputStyle.rowHeightHeader * 0.8 - height: InputStyle.rowHeightHeader - anchors.right: parent.right - anchors.rightMargin: InputStyle.panelMargin - - Rectangle { - id: avatarImage - - anchors.centerIn: parent - width: avatar.width - height: avatar.width - color: InputStyle.fontColor - radius: width*0.5 - antialiasing: true - - MouseArea { - anchors.fill: parent - onClicked: { - if ( __merginApi.userAuth.hasAuthData() && __merginApi.apiVersionStatus === MerginApiStatus.OK ) { - __merginApi.getUserInfo() - - if ( __merginApi.apiSupportsSubscriptions ) { - __merginApi.getServiceInfo() - } - - if ( __merginApi.serverType === MerginServerType.OLD ) { - stackView.push( accountPanelComp ) - } - else { - stackView.push( workspaceAccountPageComp ) - } - - } - else { - root.openAuthPanel() - } - } - } - - Image { - id: userIcon - - anchors.centerIn: avatarImage - source: InputStyle.accountIcon - height: avatarImage.height * 0.8 - width: height - sourceSize.width: width - sourceSize.height: height - fillMode: Image.PreserveAspectFit - } - - ColorOverlay { - anchors.fill: userIcon - source: userIcon - color: "#FFFFFF" - } - } - } - } - - background: Rectangle { - anchors.fill: parent - color: InputStyle.clrPanelMain - } - - Item { - id: pageContent - - anchors.fill: parent - - states: [ - State { - name: "local" - }, - State { - name: "created" - }, - State { - name: "shared" - }, - State { - name: "public" - } - ] - - state: root.visible ? "local" : "" - - onStateChanged: { - __merginApi.pingMergin() - projectsPage.refreshProjectList() - pageFooter.setActiveButton( pageContent.state ) - } - - Connections { - target: root - function onVisibleChanged() { - if ( root.visible ) { - pageContent.state = "local" - } - else { - pageContent.state = "" - } - } - - function onResetView() { - if ( pageContent.state === "created" || pageContent.state === "shared" ) - pageContent.state = "local" - } - - function onRefreshProjects() { - projectsPage.refreshProjectList() - } - } - - StackLayout { - id: projectListLayout - - anchors.fill: parent - currentIndex: pageFooter.currentIndex - - ProjectListPage { - id: localProjectsPage - - projectModelType: ProjectsModel.LocalProjectsModel - activeProjectId: root.activeProjectId - list.visible: !stackView.pending - - onOpenProjectRequested: function( projectFilePath ) { - setupProjectOpen( projectFilePath ) - } - onShowLocalChangesRequested: function( projectId ) { - showChanges( projectId ) - } - list.onActiveProjectDeleted: setupProjectOpen( "" ) - } - - ProjectListPage { - id: createdProjectsPage - - projectModelType: ProjectsModel.CreatedProjectsModel - activeProjectId: root.activeProjectId - list.visible: !stackView.pending - - onOpenProjectRequested: function( projectFilePath ) { - setupProjectOpen( projectFilePath ) - } - onShowLocalChangesRequested: function( projectId ) { - showChanges( projectId ) - } - list.onActiveProjectDeleted: setupProjectOpen( "" ) - } - - ProjectListPage { - id: sharedProjectsPage - - projectModelType: ProjectsModel.SharedProjectsModel - activeProjectId: root.activeProjectId - list.visible: !stackView.pending - - onOpenProjectRequested: function( projectFilePath ) { - setupProjectOpen( projectFilePath ) - } - onShowLocalChangesRequested: function( projectId ) { - showChanges( projectId ) - } - list.onActiveProjectDeleted: setupProjectOpen( "" ) - } - - ProjectListPage { - id: publicProjectsPage - - projectModelType: ProjectsModel.PublicProjectsModel - activeProjectId: root.activeProjectId - list.visible: !stackView.pending - - onOpenProjectRequested: function( projectFilePath ) { - setupProjectOpen( projectFilePath ) - } - onShowLocalChangesRequested: function( projectId ) { - showChanges( projectId ) - } - list.onActiveProjectDeleted: function() { - setupProjectOpen( "" ) - } - } - } - } - - footer: TabBar { - id: pageFooter - - property int itemSize: pageFooter.height * 0.8 - - function setActiveButton( state ) { - switch( state ) { - case "local": pageFooter.setCurrentIndex( 0 ); break - case "created": pageFooter.setCurrentIndex( 1 ); break - case "shared": pageFooter.setCurrentIndex( 2 ); break - case "public": pageFooter.setCurrentIndex( 3 ); break - } - } - - spacing: 0 - contentHeight: InputStyle.rowHeightHeader - - TabButton { - id: localProjectsBtn - - background: Rectangle { - anchors.fill: parent - color: InputStyle.fontColor - } - - MainPanelButton { - id: localProjectsInnerBtn - - text: qsTr("Home") - imageSource: InputStyle.homeIcon - width: pageFooter.itemSize - - handleClicks: false - faded: pageFooter.currentIndex !== localProjectsBtn.TabBar.index - } - - onClicked: pageContent.state = "local" - } - - TabButton { - id: createdProjectsBtn - - background: Rectangle { - anchors.fill: parent - color: InputStyle.fontColor - } - - MainPanelButton { - id: createdProjectsInnerBtn - - text: qsTr("My projects") - imageSource: InputStyle.accountIcon - width: pageFooter.itemSize - - handleClicks: false - faded: pageFooter.currentIndex !== createdProjectsBtn.TabBar.index - } - - onClicked: pageContent.state = "created" - } - - TabButton { - id: sharedProjectsBtn - - background: Rectangle { - anchors.fill: parent - color: InputStyle.fontColor - } - - MainPanelButton { - id: sharedProjectsInnerBtn - - imageSource: InputStyle.accountMultiIcon - width: pageFooter.itemSize - text: parent.width > sharedProjectsInnerBtn.width * 2 ? qsTr("Shared with me") : qsTr("Shared") - - handleClicks: false - faded: pageFooter.currentIndex !== sharedProjectsBtn.TabBar.index - } - - onClicked: pageContent.state = "shared" - } - - TabButton { - id: publicProjectsBtn - - background: Rectangle { - anchors.fill: parent - color: InputStyle.fontColor - } - - MainPanelButton { - id: publicProjectsInnerBtn - - text: qsTr("Explore") - imageSource: InputStyle.exploreIcon - width: pageFooter.itemSize - - handleClicks: false - faded: pageFooter.currentIndex !== publicProjectsBtn.TabBar.index - } - - onClicked: pageContent.state = "public" - } - } - - // Other components - - Connections { - target: __projectWizard - function onProjectCreationFailed(message) { - __inputUtils.showNotification(message) - stackView.pending = false - } - function onProjectCreated( projectDir, projectName ) { - if (stackView.currentItem.objectName === "projectWizard") { - __inputUtils.log( - "Create project", - "Local project " + projectName + " created at path: " + projectDir + " by " - + ( __merginApi.userAuth ? __merginApi.userAuth.username : "unknown" ) ) - stackView.popOnePageOrClose() - } - } - } - } - } - Component { id: workspaceProjectsPanelComp @@ -622,7 +254,7 @@ Item { __merginApi.refreshUserData() if ( __merginApi.serverType === MerginServerType.OLD ) { - stackView.push( accountPanelComp ) + __inputUtils.showNotification( qsTr( "Unsupported server, please contact your server administrator." ) ) } else { stackView.push( workspaceAccountPageComp ) @@ -731,7 +363,7 @@ Item { projectModelType: ProjectsModel.WorkspaceProjectsModel activeProjectId: root.activeProjectId - list.visible: !stackView.pending + list.visible: !stackView.pending && __merginApi.serverType !== MerginServerType.OLD onOpenProjectRequested: function( projectFilePath ) { setupProjectOpen( projectFilePath ) @@ -747,7 +379,7 @@ Item { projectModelType: ProjectsModel.PublicProjectsModel activeProjectId: root.activeProjectId - list.visible: !stackView.pending + list.visible: !stackView.pending && __merginApi.serverType !== MerginServerType.OLD onOpenProjectRequested: function( projectFilePath ) { setupProjectOpen( projectFilePath ) @@ -898,30 +530,6 @@ Item { } } - Component { - id: accountPanelComp - - AccountPage { - id: accountPanel - height: root.height - width: root.width - visible: true - onBack: { - stackView.popOnePageOrClose() - } - onManagePlansClicked: manageSubscriptionPlans() - onSignOutClicked: { - __merginApi.signOut() - stackView.pop( null ) - root.resetView() - } - onAccountDeleted: { - stackView.popOnePageOrClose() - root.resetView() - } - } - } - Component { id: workspaceAccountPageComp @@ -1087,14 +695,7 @@ Item { function onRegistrationSucceeded() { stackView.pending = false - - if ( __merginApi.serverType !== MerginServerType.OLD ) { - stackView.push( registrationFinishComponent ) - } - } - - function onServerTypeChanged( serverType ) { - stackView.switchUI() + stackView.push( registrationFinishComponent ) } function onActiveWorkspaceChanged() { diff --git a/app/qml/components/MMBackButton.qml b/app/qml/components/MMBackButton.qml new file mode 100644 index 000000000..658ef2537 --- /dev/null +++ b/app/qml/components/MMBackButton.qml @@ -0,0 +1,35 @@ +/*************************************************************************** + * * + * 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 +import "." + +RoundButton { + id: control + + implicitWidth: 40 * __dp + implicitHeight: 40 * __dp + + property color color: __style.whiteColor + property color hoverColor: __style.mediumGreenColor + + contentItem: MMIcon { + id: icon + + source: __style.backIcon + color: __style.forestColor + } + + background: Rectangle { + color: control.down || control.hovered ? control.hoverColor : control.color + radius: control.implicitHeight / 2 + } +} diff --git a/app/qml/components/MMDrawer.qml b/app/qml/components/MMDrawer.qml index 7a2ad8f3e..0dace5731 100644 --- a/app/qml/components/MMDrawer.qml +++ b/app/qml/components/MMDrawer.qml @@ -17,6 +17,7 @@ Drawer { property alias picture: picture.source property alias title: title.text + property alias bigTitle: bigTitle.text property alias description: description.text property alias boundedDescription: boundedDescription.text property alias primaryButton: primaryButton.text @@ -55,16 +56,37 @@ Drawer { rightPadding: 20 * __dp bottomPadding: 20 * __dp - Image { - id: closeButton + Row { + width: parent.width + spacing: 10 * __dp + + Item { + id: emptyItem + + width: closeButton.width + height: 1 + } + + Text { + id: title + + anchors.verticalCenter: parent.verticalCenter + width: parent.width - emptyItem.width - closeButton.width - mainColumn.leftPadding - mainColumn.rightPadding - 2 * parent.spacing + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + font: __style.t2 + color: __style.forestColor + } + + Image { + id: closeButton - source: __style.closeButtonIcon - anchors.right: parent.right - anchors.rightMargin: 20 * __dp + source: __style.closeButtonIcon - MouseArea { - anchors.fill: parent - onClicked: control.visible = false + MouseArea { + anchors.fill: parent + onClicked: control.visible = false + } } } @@ -75,7 +97,7 @@ Drawer { } Text { - id: title + id: bigTitle anchors.horizontalCenter: parent.horizontalCenter font: __style.t1 diff --git a/app/qml/components/MMHeader.qml b/app/qml/components/MMHeader.qml new file mode 100644 index 000000000..ad89a06f1 --- /dev/null +++ b/app/qml/components/MMHeader.qml @@ -0,0 +1,80 @@ +/*************************************************************************** + * * + * 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 + +Row { + id: root + + property string headerTitle + property int step: -1 // -1 no step bar shown; 1, 2, 3 + property bool backVisible: true + property color backColor: __style.whiteColor + property font titleFont: __style.t4 + + signal backClicked + + width: parent.width + spacing: 5 * __dp + + Row { + id: backButton + + width: 60 * __dp + + MMBackButton { + visible: root.backVisible + color: root.backColor + + onClicked: root.backClicked() + } + + Item { + id: space + + width: 20 * __dp + height: 1 + visible: backButton.visible + } + } + + Text { + anchors.verticalCenter: parent.verticalCenter + width: { + if(backButton.visible || progressBar.visible) + return root.width - (backButton.visible ? backButton.width + root.spacing : 0) + - progressBar.width - root.spacing + return root.width + } + text: root.headerTitle + font: root.titleFont + color: __style.forestColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + + Item { + width: progressBar.width + height: progressBar.height + anchors.verticalCenter: parent.verticalCenter + + MMProgressBar { + id: progressBar + + width: 60 * __dp + height: 4 * __dp + + color: __style.grassColor + progressColor: __style.forestColor + visible: root.step > 0 + position: root.step > 0 ? root.step / 3 : 0 + } + } +} diff --git a/app/qml/components/MMHlineText.qml b/app/qml/components/MMHlineText.qml new file mode 100644 index 000000000..eb07ee277 --- /dev/null +++ b/app/qml/components/MMHlineText.qml @@ -0,0 +1,45 @@ +/*************************************************************************** + * * + * 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 + +Row { + id: root + + required property string title + property color lineColor: __style.grayColor + property color textColor: __style.nightColor + + spacing: 15 + + Rectangle { + id: leftLine + + width: (root.width - text.width) / 2 - root.spacing + height: 1 + anchors.verticalCenter: parent.verticalCenter + color: root.lineColor + } + + Text { + id: text + + text: root.title + font: __style.t3 + color: root.textColor + wrapMode: Text.WordWrap + } + + Rectangle { + width: leftLine.width + height: leftLine.height + color: leftLine.color + anchors.verticalCenter: parent.verticalCenter + } +} diff --git a/app/qml/components/MMIconCheckBoxHorizontal.qml b/app/qml/components/MMIconCheckBoxHorizontal.qml new file mode 100644 index 000000000..4d045b05d --- /dev/null +++ b/app/qml/components/MMIconCheckBoxHorizontal.qml @@ -0,0 +1,55 @@ +/*************************************************************************** + * * + * 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 +import "." + +CheckBox { + id: control + + property string sourceIcon: "" + property bool small: false + + height: (control.small ? 50 : 80) * __dp + + indicator: Rectangle { + id: iconBgRectangle + width: (control.small ? 24 : 40) * __dp + height: (control.small ? 24 : 40) * __dp + x: 20 * __dp + y: control.height / 2 - height / 2 + radius: width / 2 + color: control.checked ? __style.whiteColor : __style.lightGreenColor + + MMIcon { + id: icon + width: (control.small ? 16 : 24) * __dp + height: (control.small ? 16 : 24) * __dp + anchors.centerIn: parent + source: control.sourceIcon + color: __style.forestColor + } + } + + contentItem: Text { + text: control.text + font: __style.t3 + color: control.checked ? __style.whiteColor : __style.nightColor + verticalAlignment: Text.AlignVCenter + leftPadding: control.indicator.width + 30 * __dp + rightPadding: 20 * __dp + } + + background: Rectangle { + radius: __style.inputRadius + color: control.checked ? __style.forestColor: __style.whiteColor + } +} diff --git a/app/qml/components/MMIconCheckBoxVertical.qml b/app/qml/components/MMIconCheckBoxVertical.qml new file mode 100644 index 000000000..888d34c64 --- /dev/null +++ b/app/qml/components/MMIconCheckBoxVertical.qml @@ -0,0 +1,73 @@ +/*************************************************************************** + * * + * 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 +import "." + +CheckBox { + id: control + + property string sourceIcon: "" + property color bgColorIcon: __style.forestColor + + width: 170 * __dp + height: 158 * __dp + + indicator: Item {} + + contentItem: Column { + padding: 20 * __dp + spacing: 10 * __dp + + Item { + width: parent.width + height: 50 * __dp + anchors.horizontalCenter: parent.horizontalCenter + + Rectangle { + id: iconBgRectangle + + width: 50 * __dp + height: width + radius: width / 2 + anchors.horizontalCenter: parent.horizontalCenter + color: control.bgColorIcon + + MMIcon { + id: icon + + width: (control.small ? 16 : 24) * __dp + height: (control.small ? 16 : 24) * __dp + anchors.centerIn: parent + source: control.sourceIcon + } + } + } + Text { + width: parent.width - 2 * parent.padding + height: control.height - 2 * parent.padding - parent.spacing - iconBgRectangle.height + anchors.horizontalCenter: parent.horizontalCenter + + text: control.text + font: __style.t3 + color: control.checked ? __style.whiteColor : __style.nightColor + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + lineHeight: 1.5 + } + } + + background: Rectangle { + radius: __style.inputRadius + color: control.checked ? __style.forestColor: __style.whiteColor + } +} diff --git a/app/qml/components/MMLink.qml b/app/qml/components/MMLink.qml index f7ff33e8d..c407874fa 100644 --- a/app/qml/components/MMLink.qml +++ b/app/qml/components/MMLink.qml @@ -15,29 +15,43 @@ import "." Button { id: control - contentItem: Row { - anchors.centerIn: control - spacing: 10 * __dp - - Text { - id: text - - font: __style.t3 - text: control.text - color: control.enabled ? control.down || control.hovered ? __style.nightColor : __style.forestColor : __style.mediumGreenColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter - } + property url leftIcon: "" + property url rightIcon: "" + + height: 50 * __dp + + contentItem: Item { + Row { + anchors.centerIn: parent + spacing: 5 * __dp + + MMIcon { + source: control.leftIcon + color: text.color + } + + Text { + id: text + + font: __style.t3 + text: control.text + color: control.enabled ? control.down || control.hovered ? __style.nightColor : __style.forestColor : __style.mediumGreenColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + anchors.verticalCenter: parent.verticalCenter + } - MMIcon { - source: __style.arrowLinkRightIcon - color: text.color + MMIcon { + source: control.rightIcon + color: text.color + } } } background: Rectangle { + width: control.width + height: control.height color: __style.transparentColor } } 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/MMMorePhoto.qml b/app/qml/components/MMMorePhoto.qml new file mode 100644 index 000000000..5a1a08fe6 --- /dev/null +++ b/app/qml/components/MMMorePhoto.qml @@ -0,0 +1,108 @@ +/*************************************************************************** + * * + * 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 Qt5Compat.GraphicalEffects + +Row { + id: control + + required property url photoUrl + property int hiddenPhotoCount: 0 + property int space: 0 + + signal clicked() + + // left space + Item { width: control.space; height: 1 } + + Image { + id: image + + width: control.width - control.space + height: width + + source: control.photoUrl + asynchronous: true + layer.enabled: true + layer { + effect: OpacityMask { + maskSource: Item { + width: image.width + height: width + Rectangle { + anchors.centerIn: parent + width: image.width + height: parent.height + radius: 20 * __dp + } + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked() + } + + Rectangle { + anchors.centerIn: parent + width: image.width + height: parent.height + + radius: 20 * __dp + color: __style.transparentColor + border.color: __style.forestColor + border.width: 1 * __dp + } + + Image { + id: bluredImage + + anchors.fill: parent + source: image.source + + asynchronous: true + layer.enabled: true + + layer { + effect: FastBlur { + anchors.fill: bluredImage + source: bluredImage + radius: 32 + } + } + } + + Column { + anchors.centerIn: parent + + Image { + source: __style.morePhotosIcon + anchors.horizontalCenter: parent.horizontalCenter + } + + Text { + font: __style.t4 + text: qsTr("+%1 more").arg(control.hiddenPhotoCount) + color: __style.whiteColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + onStatusChanged: { + if (status === Image.Error) { + console.error("MMMorePhoto: Error loading image"); + } + } + } +} diff --git a/app/qml/components/MMPasswordInput.qml b/app/qml/components/MMPasswordInput.qml index 5e045c115..9a0ff0d58 100644 --- a/app/qml/components/MMPasswordInput.qml +++ b/app/qml/components/MMPasswordInput.qml @@ -14,10 +14,19 @@ import QtQuick.Controls.Basic Column { id: control + signal textEdited + + enum MsgShowBehaviour { + Never, + OnNotMatchingRegex, + Always + } + property alias title: titleItem.text property alias text: textField.text property alias placeholderText: textField.placeholderText - required property string regexp + property string regexp: '.*' + property int msgShowBehaviour: MMPasswordInput.OnNotMatchingRegex property url iconSource: "" property string warningMsg property string errorMsg @@ -77,6 +86,10 @@ Column { background: Rectangle { color: __style.transparentColor } + + onTextEdited: { + control.textEdited() + } } MMIcon { @@ -127,6 +140,11 @@ Column { } function isPasswordCorrect(pwd) { + if (msgShowBehaviour === MMPasswordInput.Never) + return true + else if (msgShowBehaviour === MMPasswordInput.Always) + return false + let pwdRegexp = new RegExp(control.regexp) return pwdRegexp.test(pwd) } diff --git a/app/qml/components/MMPhoto.qml b/app/qml/components/MMPhoto.qml new file mode 100644 index 000000000..6fd3eb873 --- /dev/null +++ b/app/qml/components/MMPhoto.qml @@ -0,0 +1,61 @@ +/*************************************************************************** + * * + * 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 Qt5Compat.GraphicalEffects +import "." + +Image { + id: control + + property url photoUrl + + signal clicked( var path ) + + height: width + source: control.photoUrl + asynchronous: true + layer.enabled: true + layer { + effect: OpacityMask { + maskSource: Item { + width: control.width + height: control.height + Rectangle { + anchors.centerIn: parent + width: parent.width + height: parent.height + radius: 20 * __dp + } + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked(control.photoUrl) + } + + Rectangle { + anchors.centerIn: parent + width: parent.width + height: parent.height + radius: 20 * __dp + color: __style.transparentColor + border.color: __style.forestColor + border.width: 1 * __dp + } + + onStatusChanged: { + if (status === Image.Error) { + console.error("MMPhoto: Error loading image: " + control.photoUrl); + } + } +} diff --git a/app/qml/components/MMPhotoGallery.qml b/app/qml/components/MMPhotoGallery.qml new file mode 100644 index 000000000..b0de689cd --- /dev/null +++ b/app/qml/components/MMPhotoGallery.qml @@ -0,0 +1,122 @@ +/*************************************************************************** + * * + * 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 + +Item { + id: control + + width: parent.width + height: column.height + + required property var model + property string title + property string warningMsg + property string errorMsg + property int maxVisiblePhotos: 5 // -1 for showing all photos + + signal showAll() + signal clicked( var path ) + + Column { + id: column + + padding: 20 * __dp + spacing: 10 * __dp + width: parent.width - 40 * __dp + + Item { + width: parent.width + height: 15 * __dp + + Text { + width: column.width - showAllButton.width - 10 * __dp + + text: control.title + font: __style.p6 + elide: Text.ElideRight + color: __style.nightColor + } + + Text { + id: showAllButton + + anchors.right: parent.right + + text: qsTr("Show all") + font: __style.t4 + color: __style.forestColor + + MouseArea { + anchors.fill: parent + onClicked: control.showAll() + } + } + } + + ListView { + id: rowView + + height: 120 * __dp + width: parent.width + spacing: control.maxVisiblePhotos !== 0 ? 20 * __dp : 0 + orientation: ListView.Horizontal + + model: { + if(control.maxVisiblePhotos >= 0 && control.model.length > control.maxVisiblePhotos) { + return control.model.slice(0, control.maxVisiblePhotos) + } + return control.model + } + + delegate: MMPhoto { + width: rowView.height + + photoUrl: model.modelData + + onClicked: function(path) { control.clicked(path) } + } + + footer: MMMorePhoto { + width: visible ? rowView.height + rowView.spacing: 0 + + hiddenPhotoCount: control.model.length - control.maxVisiblePhotos + visible: control.maxVisiblePhotos >= 0 && control.model.length > control.maxVisiblePhotos + photoUrl: visible ? model[control.maxVisiblePhotos] : "" + space: visible ? rowView.spacing : 0 + + onClicked: control.showAll() + } + } + + Row { + id: msgRow + + spacing: 4 * __dp + + MMIcon { + id: msgIcon + + source: visible ? __style.errorIcon : "" + color: errorMsg.length > 0 ? __style.negativeColor : __style.warningColor + visible: errorMsg.length > 0 || warningMsg.length > 0 + } + + Text { + width: column.width - msgRow.spacing - msgIcon.width + + text: errorMsg.length > 0 ? errorMsg : warningMsg + font: __style.t4 + wrapMode: Text.WordWrap + visible: errorMsg.length > 0 || warningMsg.length > 0 + } + } + } +} diff --git a/app/qml/components/MMProgressBar.qml b/app/qml/components/MMProgressBar.qml index d3df3f6e3..9c52e9af4 100644 --- a/app/qml/components/MMProgressBar.qml +++ b/app/qml/components/MMProgressBar.qml @@ -14,6 +14,7 @@ Rectangle { id: control required property real position // [0 - 1] + property color progressColor: __style.grassColor width: parent.width height: 12 * __dp @@ -23,7 +24,7 @@ Rectangle { Rectangle { width: parent.width * control.position height: parent.height - color: __style.grassColor + color: control.progressColor radius: height / 2 } } 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/app/qml/components/MMTextBubble.qml b/app/qml/components/MMTextBubble.qml new file mode 100644 index 000000000..f72270e99 --- /dev/null +++ b/app/qml/components/MMTextBubble.qml @@ -0,0 +1,74 @@ +/*************************************************************************** + * * + * 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.Layouts + +Item { + id: root + + height: row.height + + required property string title + required property string description + + Rectangle { + width: root.width + height: row.height + color: __style.whiteColor + radius: __style.inputRadius + } + + Row { + id: row + + padding: 20 * __dp + spacing: 10 * __dp + + Rectangle { + width: 50 * __dp + height: width + radius: height / 2 + color: __style.forestColor + + Image { + id: icon + anchors.centerIn: parent + source: __style.bubbleIcon + } + } + + Column { + id: column + + width: root.width - icon.width - 4 * row.spacing - 2 * row.padding + spacing: 10 * __dp + + Text { + width: parent.width + + text: root.title + font: __style.t3 + color: __style.deepOceanColor + wrapMode: Label.WordWrap + } + + Text { + width: column.width + + text: root.description + font: __style.p6 + color: __style.deepOceanColor + wrapMode: Label.WordWrap + lineHeight: 1.5 + } + } + } +} diff --git a/app/qml/inputs/MMAbstractEditor.qml b/app/qml/inputs/MMAbstractEditor.qml new file mode 100644 index 000000000..43de20e7d --- /dev/null +++ b/app/qml/inputs/MMAbstractEditor.qml @@ -0,0 +1,193 @@ +/*************************************************************************** + * * + * 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 QtQml.Models +import QtQuick.Layouts + +import "../components" +import "." + +Item { + id: root + + signal contentClicked() + signal leftActionClicked() + signal rightActionClicked() + + property alias title: titleItem.text + property alias leftAction: leftActionContainer.children + property alias content: contentContainer.children + property alias rightAction: rightActionContainer.children + property string warningMsg + property string errorMsg + property bool hasFocus: false + property color bgColor: __style.whiteColor + property bool hasCheckbox: false + property bool checkboxChecked: false + + readonly property real spacing: 15 * __dp + + width: parent.width + height: mainColumn.height + + Column { + id: mainColumn + + spacing: 6 * __dp + anchors.left: parent.left + anchors.right: parent.right + + Row { + id: titleRow + + spacing: 4 * __dp + width: parent.width + visible: titleItem.text.length > 0 + + MMCheckBox { + id: checkbox + + small: true + visible: root.hasCheckbox + checked: root.checkboxChecked + } + Text { + id: titleItem + + width: parent.width - checkbox.width - titleRow.spacing + + font: __style.p6 + wrapMode: Text.WordWrap + } + } + + Item { + height: 50 * __dp + anchors.left: parent.left + anchors.right: parent.right + + Rectangle { + id: background + + width: parent.width + height: parent.height + + border.width: 2 * __dp + color: root.bgColor + radius: __style.inputRadius + border.color: { + if (root.hasFocus) { + if (errorMsg.length > 0) { + return __style.negativeColor + } + else if (warningMsg.length > 0) { + return __style.warningColor + } + return __style.forestColor + } + return __style.transparentColor + } + } + + Row { + height: parent.height + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: root.spacing + anchors.rightMargin: root.spacing + + Item { + id: leftActionContainer + + property bool actionAllowed: leftActionContainer.children.length > 1 + + height: parent.height + width: actionAllowed ? height/2 : 0 + + Item { + width: leftActionContainer.actionAllowed ? parent.width + root.spacing/2 : 0 + height: parent.height + anchors.centerIn: parent + + MouseArea { + anchors.fill: parent + onReleased: root.leftActionClicked() + } + } + } + + Item { + id: contentContainer + + height: parent.height + width: parent.width - (leftActionContainer.actionAllowed ? leftActionContainer.width : 0) - (rightActionContainer.actionAllowed ? rightActionContainer.width : 0) + + MouseArea { + anchors.fill: parent + + onClicked: { + root.contentClicked() + } + } + } + + Item { + id: rightActionContainer + + property bool actionAllowed: rightActionContainer.children.length > 1 + + height: parent.height + width: actionAllowed ? height/2 : 0 + + Item { + width: rightActionContainer.actionAllowed ? parent.width + root.spacing/2 : 0 + height: parent.height + anchors.centerIn: parent + + MouseArea { + anchors.fill: parent + onReleased: root.rightActionClicked() + } + } + } + } + } + + Item { + id: messageItem + + width: parent.width + height: msgRow.height + + Row { + id: msgRow + + spacing: 4 * __dp + + MMIcon { + id: msgIcon + + source: visible ? __style.errorIcon : "" + color: errorMsg.length > 0 ? __style.negativeColor : __style.warningColor + visible: errorMsg.length > 0 || warningMsg.length > 0 + } + Text { + width: messageItem.width - msgRow.spacing - msgIcon.width + + text: root.errorMsg.length > 0 ? root.errorMsg : root.warningMsg + font: __style.t4 + wrapMode: Text.WordWrap + visible: root.errorMsg.length > 0 || root.warningMsg.length > 0 + } + } + } + } +} diff --git a/app/qml/components/MMCheckBox.qml b/app/qml/inputs/MMCheckBox.qml similarity index 62% rename from app/qml/components/MMCheckBox.qml rename to app/qml/inputs/MMCheckBox.qml index 1c6f109ce..d1e6f18ce 100644 --- a/app/qml/components/MMCheckBox.qml +++ b/app/qml/inputs/MMCheckBox.qml @@ -10,21 +10,32 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic +import "../components" CheckBox { id: control - checked: true + property bool small: false + + width: (control.small ? 16 : 24) * __dp + height: control.width indicator: Rectangle { - implicitWidth: 24 - implicitHeight: 24 - x: control.leftPadding - y: parent.height / 2 - height / 2 - radius: 5 - color: enabled ? ( control.checked ? __style.grassColor: __style.whiteColor ) : __style.whiteColor - border.color: enabled ? ( control.checked ? __style.grassColor: __style.forestColor ) : __style.mediumGreenColor - border.width: control.hovered ? 2.5 : 2 + width: control.width + height: control.height + y: control.height / 2 - height / 2 + radius: 5 * __dp + color: (enabled && control.checked) ? __style.grassColor: __style.whiteColor + border.color: { + if(enabled) { + if(checked) { + return __style.grassColor + } + return __style.forestColor + } + return __style.mediumGreenColor + } + border.width: (control.hovered ? 2.5 : 2) * __dp MMIcon { id: icon @@ -33,6 +44,7 @@ CheckBox { source: __style.checkmarkIcon color: control.enabled ? __style.forestColor : __style.mediumGreenColor visible: control.checked + scale: control.width / (24 * __dp) } } @@ -41,6 +53,6 @@ CheckBox { font: __style.p5 color: icon.color verticalAlignment: Text.AlignVCenter - leftPadding: control.indicator.width + control.spacing + leftPadding: control.indicator.width } } diff --git a/app/qml/inputs/MMInputEditor.qml b/app/qml/inputs/MMInputEditor.qml new file mode 100644 index 000000000..587adbd89 --- /dev/null +++ b/app/qml/inputs/MMInputEditor.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 +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import "../components" + +MMAbstractEditor { + id: root + + property var parentValue: parent.value ?? "" + property bool parentValueIsNull: parent.valueIsNull ?? false + property bool isReadOnly: parent.readOnly ?? false + + property alias placeholderText: textField.placeholderText + property alias text: textField.text + + signal editorValueChanged( var newValue, var isNull ) + + hasFocus: textField.activeFocus + + content: TextField { + id: textField + + anchors.fill: parent + + text: root.parentValue + color: root.enabled ? __style.nightColor : __style.mediumGreenColor + placeholderTextColor: __style.nightAlphaColor + font: __style.p5 + hoverEnabled: true + + background: Rectangle { + color: __style.transparentColor + } + } + + rightAction: MMIcon { + id: rightIcon + + height: parent.height + + source: __style.xMarkIcon + color: root.enabled ? __style.forestColor : __style.mediumGreenColor + visible: textField.activeFocus && textField.text.length>0 + } + + onRightActionClicked: textField.text = "" +} diff --git a/app/qml/inputs/MMPasswordEditor.qml b/app/qml/inputs/MMPasswordEditor.qml new file mode 100644 index 000000000..5385c9da4 --- /dev/null +++ b/app/qml/inputs/MMPasswordEditor.qml @@ -0,0 +1,53 @@ +/*************************************************************************** + * * + * 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 +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import "../components" + +MMAbstractEditor { + id: root + + property alias placeholderText: textField.placeholderText + readonly property alias text: textField.text + + hasFocus: textField.activeFocus + + content: TextField { + id: textField + + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + + color: root.enabled ? __style.nightColor : __style.mediumGreenColor + placeholderTextColor: __style.nightAlphaColor + font: __style.p5 + hoverEnabled: true + echoMode: eyeButton.pressed ? TextInput.Normal : TextInput.Password + background: Rectangle { + color: __style.transparentColor + } + } + + rightAction: MMIcon { + id: eyeButton + + property bool pressed: false + + height: parent.height + + source: pressed ? __style.hideIcon : __style.showIcon + color: root.enabled ? __style.forestColor : __style.mediumGreenColor + } + + onRightActionClicked: eyeButton.pressed = !eyeButton.pressed +} diff --git a/app/qml/inputs/MMSliderEditor.qml b/app/qml/inputs/MMSliderEditor.qml new file mode 100644 index 000000000..35545f411 --- /dev/null +++ b/app/qml/inputs/MMSliderEditor.qml @@ -0,0 +1,98 @@ +/*************************************************************************** + * * + * 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 +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import ".." + +MMAbstractEditor { + id: root + + property var parentValue: parent.value + property bool parentValueIsNull: parent.valueIsNull ?? false + property bool isReadOnly: parent.readOnly ?? false + + property int precision: 1 //config["Precision"] + required property real from //getRange(config["Min"], -max_range) + required property real to //getRange(config["Max"], max_range) + property real step: 1 //config["Step"] ? config["Step"] : 1 + property string suffix: "" //config["Suffix"] ? config["Suffix"] : "" + property var locale: Qt.locale() + + signal editorValueChanged( var newValue, var isNull ) + + hasFocus: slider.activeFocus + + content: Item { + id: input + + anchors.fill: parent + + RowLayout { + id: rowLayout + + anchors.fill: parent + + Text { + id: valueLabel + + Layout.preferredWidth: rowLayout.width / 2 - root.spacing + Layout.maximumWidth: rowLayout.width / 2 - root.spacing + Layout.preferredHeight: input.height + Layout.maximumHeight: input.height + + elide: Text.ElideRight + text: Number( slider.value ).toFixed( precision ).toLocaleString( root.locale ) + root.suffix + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font: __style.p5 + color: __style.nightColor + } + + Slider { + id: slider + + Layout.fillWidth: true + Layout.maximumHeight: input.height + Layout.preferredHeight: input.height + + to: root.to + from: root.from + stepSize: root.step + value: root.parentValue ? root.parentValue : 0 + + onValueChanged: { root.editorValueChanged( slider.value, false ); forceActiveFocus() } + + background: Rectangle { + x: slider.leftPadding + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + width: slider.availableWidth + height: 4 * __dp + radius: 2 * __dp + + color: __style.lightGreenColor + } + + handle: Rectangle { + x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + width: 20 * __dp + height: width + radius: height / 2 + + color: root.enabled ? __style.forestColor : __style.lightGreenColor + } + } + } + } +} diff --git a/app/qml/onboarding/MMAcceptInvitation.qml b/app/qml/onboarding/MMAcceptInvitation.qml new file mode 100644 index 000000000..deabc63b5 --- /dev/null +++ b/app/qml/onboarding/MMAcceptInvitation.qml @@ -0,0 +1,124 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +import "../components" +import "../inputs" + +Page { + id: root + + required property string user + required property string workspace + + signal backClicked + signal continueClicked + signal createWorkspaceClicked + + readonly property real hPadding: width < __style.maxPageWidth + ? 20 * __dp + : (20 + (width - __style.maxPageWidth) / 2) * __dp + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + ScrollView { + width: parent.width + height: parent.height + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + + Column { + id: mainColumn + + width: root.width + spacing: 20 * __dp + leftPadding: root.hPadding + rightPadding: root.hPadding + topPadding: 100 * __dp + bottomPadding: 20 * __dp + + Item { + width: parent.width - 2 * root.hPadding + height: bg.height + + Image { + id: bg + + anchors.horizontalCenter: parent.horizontalCenter + source: __style.acceptInvitationImage + + Image { + id: fg + x: ( bg.width - fg.width ) / 2 + 7 + y: ( bg.height - fg.height ) / 2 + 5 + source: __style.acceptInvitationLogoImage + } + } + } + + Text { + width: parent.width - 2 * root.hPadding + text: qsTr("You have been invited to workspace") + font: __style.h3 + color: __style.forestColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.2 + } + + Text { + width: parent.width - 2 * root.hPadding + text: qsTr("User %1 has invited you to join his workspace").arg(root.user) + font: __style.p5 + color: __style.nightColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.5 + } + + Item { width: 1; height: 1 } + + Text { + width: parent.width - 2 * root.hPadding + text: root.workspace + font: __style.t1 + color: __style.nightColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.5 + } + + Item { width: 1; height: 50 } + + MMButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Join workspace") + + onClicked: root.continueClicked() + } + + MMHlineText { + width: parent.width - 2 * root.hPadding + title: qsTr("or") + } + + MMLinkButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Create new workspace") + + onClicked: root.createWorkspaceClicked() + } + } + } +} diff --git a/app/qml/onboarding/MMCreateWorkspace.qml b/app/qml/onboarding/MMCreateWorkspace.qml new file mode 100644 index 000000000..ed9fe15bd --- /dev/null +++ b/app/qml/onboarding/MMCreateWorkspace.qml @@ -0,0 +1,115 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +import "../components" +import "../inputs" + +Page { + id: root + + signal continueClicked + + readonly property real hPadding: width < __style.maxPageWidth + ? 20 * __dp + : (20 + (width - __style.maxPageWidth) / 2) * __dp + + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + MMHeader { + id: header + + x: mainColumn.leftPadding + y: mainColumn.topPadding + width: parent.width - 2 * root.hPadding + backVisible: false + step: 1 + + onBackClicked: root.backClicked() + } + + ScrollView { + width: parent.width + height: parent.height - header.height - bottomColumn.height - 40 * __dp + anchors.top: header.bottom + anchors.topMargin: 20 * __dp + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + + Column { + id: mainColumn + + width: root.width + spacing: 20 * __dp + leftPadding: root.hPadding + rightPadding: root.hPadding + topPadding: 20 * __dp + bottomPadding: 20 * __dp + + Text { + width: parent.width - 2 * root.hPadding + text: qsTr("Create a workspace") + font: __style.h3 + color: __style.forestColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.2 + } + + Text { + width: parent.width - 2 * root.hPadding + text: qsTr("Workspace is a place to store your projects. Colleagues can be invited to your workspace to collaborate on projects.") + font: __style.p5 + color: __style.nightColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.5 + } + + Item { width: 1; height: 1 } + + MMInputEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Workspace name") + placeholderText: qsTr("Your Workspace") + } + } + } + + Column { + id: bottomColumn + + width: root.width + spacing: 20 * __dp + leftPadding: root.hPadding + rightPadding: root.hPadding + topPadding: 20 * __dp + bottomPadding: 20 * __dp + anchors.bottom: parent.bottom + + MMTextBubble { + width: root.width - 2 * root.hPadding + title: "Tip from Mergin Maps" + description: "A good candidate for a workspace name is the name of your team or organisation" + } + + MMButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Create workspace") + + onClicked: root.continueClicked() + } + } +} diff --git a/app/qml/onboarding/MMHowYouFoundUs.qml b/app/qml/onboarding/MMHowYouFoundUs.qml new file mode 100644 index 000000000..d2681eace --- /dev/null +++ b/app/qml/onboarding/MMHowYouFoundUs.qml @@ -0,0 +1,141 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +import "../components" +import "../inputs" + +Page { + id: root + + width: parent.width + + property string selectedText: "" + + signal backClicked + signal continueClicked(var selectedText) + + readonly property string headerTitle: qsTr("How did you learn about us?") + readonly property real hPadding: width < __style.maxPageWidth + ? 20 * __dp + : (20 + (width - __style.maxPageWidth) / 2) * __dp + + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + MMHeader { + id: header + + x: root.hPadding + y: 20 * __dp + width: root.width - 2 * root.hPadding + headerTitle: listView.contentY > -30 * __dp ? root.headerTitle : "" + step: 2 + + onBackClicked: root.backClicked() + } + + Item { + width: parent.width + height: parent.height - (listView.model.count === listView.currentIndex + 1 ? header.height + 50 * __dp : 0) + anchors.top: header.bottom + anchors.topMargin: 20 * __dp + + ListView { + id: listView + + width: parent.width - 2 * root.hPadding + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height - header.height + spacing: 10 * __dp + clip: true + + Component.onCompleted: currentIndex = -1 + + model: ListModel { + Component.onCompleted: { + listView.model.append({name: qsTr("Search engine (Google, ...)"), icon: __style.searchIcon, submenu: false}) + listView.model.append({name: qsTr("Blog"), icon: __style.termsIcon, submenu: false}) + listView.model.append({name: qsTr("Mouth"), icon: __style.mouthIcon, submenu: false}) + listView.model.append({name: qsTr("QGIS website"), icon: __style.qgisIcon, submenu: false}) + listView.model.append({name: qsTr("Application store"), icon: __style.subscriptionsIcon, submenu: false}) + listView.model.append({name: qsTr("Teacher"), icon: __style.teacherIcon, submenu: false}) + listView.model.append({name: qsTr("Conference"), icon: __style.briefcaseIcon, submenu: false}) + listView.model.append({name: qsTr("Social media"), icon: __style.socialMediaIcon, submenu: false}) + listView.model.append({name: qsTr("YouTube"), icon: __style.youtubeIcon, submenu: true}) + listView.model.append({name: qsTr("Twitter"), icon: __style.xTwitterIcon, submenu: true}) + listView.model.append({name: qsTr("Facebook"), icon: __style.facebookIcon, submenu: true}) + listView.model.append({name: qsTr("LinkedIn"), icon: __style.linkedinIcon, submenu: true}) + listView.model.append({name: qsTr("Mastodon"), icon: __style.mastodonIcon, submenu: true}) + listView.model.append({name: qsTr("Reddit"), icon: __style.redditIcon, submenu: true}) + listView.model.append({name: qsTr("Other"), icon: __style.otherIcon, submenu: false}) + } + } + + header: Text { + id: listHeader + + width: root.width - 2 * root.hPadding + padding: 20 * __dp + text: root.headerTitle + font: __style.h3 + color: __style.forestColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.2 + } + + delegate: MMIconCheckBoxHorizontal { + width: model.submenu ? listView.width - 20 * __dp : listView.width + x: model.submenu ? 20 * __dp : 0 + sourceIcon: model.icon + text: model.name + small: model.submenu + checked: listView.currentIndex === index + + onClicked: { + root.selectedText = model.name + listView.currentIndex = index + + if(listView.model.count === listView.currentIndex + 1) + listView.positionViewAtEnd() + } + } + + footer: Column { + width: root.width - 2 * root.hPadding + topPadding: 20 * __dp + visible: listView.model.count === listView.currentIndex + 1 + + MMInputEditor { + title: qsTr("Source") + placeholderText: qsTr("Please specify the source") + onTextChanged: root.selectedText = text + onVisibleChanged: if(visible) hasFocus = true + } + + Item { width: 1; height: 60 * __dp } + } + } + } + + MMButton { + width: root.width - 2 * root.hPadding + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 * __dp + text: qsTr("Continue") + + onClicked: root.continueClicked(root.selectedText) + } +} diff --git a/app/qml/onboarding/MMLogin.qml b/app/qml/onboarding/MMLogin.qml new file mode 100644 index 000000000..d7d017bb4 --- /dev/null +++ b/app/qml/onboarding/MMLogin.qml @@ -0,0 +1,166 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +import "../components" +import "../inputs" + +Page { + id: root + + width: parent.width + + signal backClicked + signal signInClicked + signal signUpClicked + signal changeServerClicked + signal forgotPasswordClicked + + readonly property real hPadding: width < __style.maxPageWidth + ? 20 * __dp + : (20 + (width - __style.maxPageWidth) / 2) * __dp + + // background as Drawer design + Rectangle { + anchors.fill: parent + color: __style.whiteColor + + Rectangle { + width: parent.width + height: 20 * __dp + color: __style.forestColor + } + + Rectangle { + width: parent.width + height: 40 * __dp + color: __style.whiteColor + radius: height / 2 + } + } + + MMHeader { + id: header + + x: mainColumn.leftPadding + y: mainColumn.topPadding + width: parent.width - 2 * root.hPadding + headerTitle: qsTr("Log In") + titleFont: __style.h3 + backColor: __style.lightGreenColor + + onBackClicked: root.backClicked() + } + + ScrollView { + width: parent.width + height: parent.height - changeServerButton.height - header.height - 60 * __dp + anchors.top: header.bottom + anchors.topMargin: 20 * __dp + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + + Column { + id: mainColumn + + width: root.width + spacing: 20 * __dp + leftPadding: root.hPadding + rightPadding: root.hPadding + topPadding: 20 * __dp + + Item { width: 1; height: 1 } + + MMInputEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Username") + bgColor: __style.lightGreenColor + } + + MMPasswordEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Password") + bgColor: __style.lightGreenColor + } + + MMLink { + width: parent.width - 2 * root.hPadding + height: 20 * __dp + text: qsTr("Forgot password?") + + onClicked: root.signInClicked() + } + + Item { width: 1; height: 1 } + + MMButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Sign in") + + onClicked: root.signInClicked() + } + + Item { width: 1; height: 1 } + + MMHlineText { + width: parent.width - 2 * root.hPadding + title: qsTr("Don't have an account?") + } + + MMLinkButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Sign up") + + onClicked: root.signUpClicked() + } + } + } + + MMLink { + id: changeServerButton + + width: parent.width + height: 50 * __dp + anchors.bottom: parent.bottom + text: "https://app.merginmaps.com/" + leftIcon: __style.globeIcon + + onClicked: changeServerDrawer.visible = true + } + + MMDrawer { + id: changeServerDrawer + + property string newServerUrl + + width: root.width < __style.maxPageWidth ? root.width : root.width - 2 * root.hPadding + x: root.width < __style.maxPageWidth ? 0 : root.hPadding + title: qsTr("Change server") + primaryButton: qsTr("Confirm") + visible: false + specialComponent: MMInputEditor { + width: changeServerDrawer.width - 40 * __dp + title: qsTr("Server address") + bgColor: __style.lightGreenColor + text: changeServerButton.text + + onTextChanged: changeServerDrawer.newServerUrl = text + } + + onPrimaryButtonClicked: { + changeServerButton.text = changeServerDrawer.newServerUrl + visible = false + + root.changeServerClicked() + } + } +} diff --git a/app/qml/onboarding/MMOnboarding.qml b/app/qml/onboarding/MMOnboarding.qml new file mode 100644 index 000000000..746bd1e17 --- /dev/null +++ b/app/qml/onboarding/MMOnboarding.qml @@ -0,0 +1,142 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +Item { + id: root + + signal backClicked + signal closeOnboarding + signal signInRequested + signal signUpRequested + signal createWorkspaceRequested + signal submitWorkspaceInfoRequested + + /* + Component { + id: onboarding + MMOnboarding { + anchors.fill: parent + + onCloseOnboarding: { + loader.active = false + } + } + } + + Loader { + id: loader + sourceComponent: onboarding + anchors.fill: parent + active: false + } + */ + + Component { + id: mmsignup + MMSignUp { + onBackClicked: { + stackView.pop() + } + + onSignInClicked: { + stackView.pop() + } + + onSignUpClicked: { + // TODO depends on invitations either create or accept invitation page + stackView.push(mmcreateworkspace) + // stackView.push(mmacceptinvitation) + } + } + } + + Component { + id: mmlogin + MMLogin { + onBackClicked: { + root.closeOnboarding() + } + + onSignInClicked: { + root.signInRequested() + } + + onSignUpClicked: { + stackView.push(mmsignup, {}) + } + + onChangeServerClicked: { + + } + } + } + + Component { + id: mmacceptinvitation + MMAcceptInvitation { + onBackClicked: { + stackView.pop() + } + + onCreateWorkspaceClicked: { + stackView.push(mmcreateworkspace, {}) + } + + onContinueClicked: { + root.closeOnboarding() + } + } + } + + Component { + id: mmcreateworkspace + MMCreateWorkspace { + + // no back button + onContinueClicked: { + stackView.push(mmhowyoufoundus, {}) + } + } + } + + Component { + id: mmhowyoufoundus + MMHowYouFoundUs { + onBackClicked: { + stackView.pop() + } + + onContinueClicked: { + stackView.push(mmwhichindustry, {}) + } + } + } + + Component { + id: mmwhichindustry + MMWhichIndustry { + onBackClicked: { + stackView.pop() + } + + onContinueClicked: { + root.submitWorkspaceInfoRequested() + } + } + } + + StackView { + id: stackView + anchors.fill: parent + initialItem: mmlogin + } +} diff --git a/app/qml/onboarding/MMSignUp.qml b/app/qml/onboarding/MMSignUp.qml new file mode 100644 index 000000000..c0e78c6ab --- /dev/null +++ b/app/qml/onboarding/MMSignUp.qml @@ -0,0 +1,159 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +import "../components" +import "../inputs" + +Page { + id: root + + width: parent.width + + signal backClicked + signal signInClicked + signal signUpClicked + signal changeServerClicked + signal forgotPasswordClicked + + readonly property real hPadding: width < __style.maxPageWidth + ? 20 * __dp + : (20 + (width - __style.maxPageWidth) / 2) * __dp + + // background as Drawer design + Rectangle { + anchors.fill: parent + color: __style.whiteColor + + Rectangle { + width: parent.width + height: 20 * __dp + color: __style.forestColor + } + + Rectangle { + width: parent.width + height: 40 * __dp + color: __style.whiteColor + radius: height / 2 + } + } + + MMHeader { + id: header + + x: mainColumn.leftPadding + y: mainColumn.topPadding + width: parent.width - 2 * root.hPadding + headerTitle: qsTr("Sign Up") + titleFont: __style.h3 + backColor: __style.lightGreenColor + + onBackClicked: root.backClicked() + } + + ScrollView { + width: parent.width + 40 * __dp + height: parent.height - header.height - 40 * __dp + anchors.top: header.bottom + anchors.topMargin: 20 * __dp + anchors.bottomMargin: 20 * __dp + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + + Column { + id: mainColumn + + width: root.width + spacing: 20 * __dp + leftPadding: root.hPadding + rightPadding: root.hPadding + topPadding: 20 * __dp + bottomPadding: 20 * __dp + + MMInputEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Username") + bgColor: __style.lightGreenColor + } + + MMInputEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Email address") + bgColor: __style.lightGreenColor + } + + MMPasswordEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Password") + bgColor: __style.lightGreenColor + } + + MMPasswordEditor { + width: parent.width - 2 * root.hPadding + title: qsTr("Confirm password") + bgColor: __style.lightGreenColor + } + + Row { + width: parent.width + spacing: 10 * __dp + + MMCheckBox { + id: checkbox + + width: 24 * __dp + anchors.verticalCenter: parent.verticalCenter + } + + Text { + width: parent.width - checkbox.width - parent.spacing - 2 * root.hPadding + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("I accept the Mergin Terms and Conditions and Privacy Policy") + font: __style.p5 + color: __style.nightColor + linkColor: __style.forestColor + wrapMode: Text.WordWrap + lineHeight: 1.5 + + onLinkActivated: function(link) { + Qt.openUrlExternally(link) + } + } + } + + Item { width: 1; height: 1 } + + MMButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Sign up") + + onClicked: root.signUpClicked() + } + + Item { width: 1; height: 1 } + + MMHlineText { + width: parent.width - 2 * root.hPadding + title: qsTr("Already have an account?") + } + + MMLinkButton { + width: parent.width - 2 * root.hPadding + text: qsTr("Sign in") + + onClicked: root.signUpClicked() + } + } + } +} diff --git a/app/qml/onboarding/MMWhichIndustry.qml b/app/qml/onboarding/MMWhichIndustry.qml new file mode 100644 index 000000000..628a4ba5e --- /dev/null +++ b/app/qml/onboarding/MMWhichIndustry.qml @@ -0,0 +1,147 @@ +/*************************************************************************** + * * + * 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.Layouts +import QtQuick.Controls + +import "../components" +import "../inputs" + +Page { + id: root + + width: parent.width + + property string selectedText: "" + + signal backClicked + signal continueClicked(var selectedText) + + readonly property string headerTitle: qsTr("In which industry do you work?") + readonly property real hPadding: width < __style.maxPageWidth + ? 20 * __dp + : (20 + (width - __style.maxPageWidth) / 2) * __dp + + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + MMHeader { + id: header + + x: root.hPadding + y: 20 * __dp + width: root.width - 2 * root.hPadding + headerTitle: listView.contentY > -30 * __dp ? root.headerTitle : "" + step: 2 + + onBackClicked: root.backClicked() + } + + Item { + width: parent.width + height: parent.height - (listView.model.count === listView.currentIndex + 1 ? header.height + 50 * __dp : 0) + anchors.top: header.bottom + anchors.topMargin: 20 * __dp + + GridView { + id: listView + + property real spacing: 10 * __dp + + width: parent.width - 2 * root.hPadding + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height - header.height + clip: true + cellHeight: 158 * __dp + listView.spacing + + onWidthChanged: { + if(listView.width < 150 * __dp) + listView.cellWidth = 150 * __dp + listView.spacing + else if(listView.width < 250 * __dp) + listView.cellWidth = listView.width + listView.spacing + else + listView.cellWidth = listView.width / 2 + } + + Component.onCompleted: currentIndex = -1 + + model: ListModel { + Component.onCompleted: { + listView.model.append({name: qsTr("Agriculture"), icon: __style.tractorIcon, colorx: __style.sunColor, color: "#F4CB46"}) + listView.model.append({name: qsTr("Archaeology"), icon: __style.archaeologyIcon, colorx: __style.sandColor, color: "#FFF4E2"}) + listView.model.append({name: qsTr("onstruction and engineering"), icon: __style.engineeringIcon, colorx: __style.roseColor, color: "#FFBABC"}) + listView.model.append({name: qsTr("Electric utilities"), icon: __style.electricityIcon, colorx: __style.nightColor, color: "#12181F"}) + listView.model.append({name: qsTr("Environmental protection"), icon: __style.environmentalIcon, colorx: __style.fieldColor, color: "#9BD1A9"}) + listView.model.append({name: qsTr("Local governments"), icon: __style.stateAndLocalIcon, colorx: __style.purpleColor, color: "#CCBDF5"}) + listView.model.append({name: qsTr("Natural resources"), icon: __style.naturalResourcesIcon, colorx: __style.earthColor, color: "#4D2A24"}) + listView.model.append({name: qsTr("Telecom"), icon: __style.telecommunicationIcon, colorx: __style.deepOceanColor, color: "#1C324A"}) + listView.model.append({name: qsTr("Transportation"), icon: __style.transportationIcon, colorx: __style.skyColor, color: "#A6CBF4"}) + listView.model.append({name: qsTr("Water utilities"), icon: __style.waterResourcesIcon, colorx: __style.lightGreenColor, color: "#EFF5F3"}) + listView.model.append({name: qsTr("Other"), icon: __style.otherIcon, colorx: __style.sunsetColor, color: "#FFB673"}) + } + } + + header: Text { + id: listHeader + + width: root.width - 2 * root.hPadding + padding: 20 * __dp + text: root.headerTitle + font: __style.h3 + color: __style.forestColor + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + lineHeight: 1.2 + } + + delegate: MMIconCheckBoxVertical { + width: listView.cellWidth - listView.spacing + sourceIcon: model.icon + text: model.name + bgColorIcon: model.color + checked: listView.currentIndex === index + + onClicked: { + root.selectedText = model.name + listView.currentIndex = index + + if(listView.model.count === listView.currentIndex + 1) + listView.positionViewAtEnd() + } + } + + footer: Column { + width: root.width - 2 * root.hPadding + topPadding: 20 * __dp + visible: listView.model.count === listView.currentIndex + 1 + + MMInputEditor { + title: qsTr("Source") + placeholderText: qsTr("Please specify the source") + onTextChanged: root.selectedText = text + onVisibleChanged: if(visible) hasFocus = true + } + + Item { width: 1; height: 60 * __dp } + } + } + } + + MMButton { + width: root.width - 2 * root.hPadding + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 * __dp + text: qsTr("Continue") + + onClicked: root.continueClicked(root.selectedText) + } +} diff --git a/app/test/testmerginapi.cpp b/app/test/testmerginapi.cpp index b5f47186b..98b137292 100644 --- a/app/test/testmerginapi.cpp +++ b/app/test/testmerginapi.cpp @@ -45,11 +45,11 @@ TestMerginApi::TestMerginApi( MerginApi *api ) mLocalProjectsModel->setLocalProjectsManager( &mApi->localProjectsManager() ); mLocalProjectsModel->setSyncManager( mSyncManager.get() ); - mCreatedProjectsModel = std::unique_ptr( new ProjectsModel ); - mCreatedProjectsModel->setModelType( ProjectsModel::CreatedProjectsModel ); - mCreatedProjectsModel->setMerginApi( mApi ); - mCreatedProjectsModel->setLocalProjectsManager( &mApi->localProjectsManager() ); - mCreatedProjectsModel->setSyncManager( mSyncManager.get() ); + mWorkspaceProjectsModel = std::unique_ptr( new ProjectsModel ); + mWorkspaceProjectsModel->setModelType( ProjectsModel::WorkspaceProjectsModel ); + mWorkspaceProjectsModel->setMerginApi( mApi ); + mWorkspaceProjectsModel->setLocalProjectsManager( &mApi->localProjectsManager() ); + mWorkspaceProjectsModel->setSyncManager( mSyncManager.get() ); } TestMerginApi::~TestMerginApi() = default; @@ -64,11 +64,17 @@ void TestMerginApi::initTestCase() TestUtils::authorizeUser( mApi, username, password ); TestUtils::selectFirstWorkspace( mApi, workspace ); } + else + { + workspace = mApi->userInfo()->activeWorkspaceName(); + } mUsername = username; // keep for later mWorkspaceName = workspace; // keep for later qDebug() << "AUTH: username:" << mUsername << ", workspace:" << mWorkspaceName; + QVERIFY( !mWorkspaceName.isEmpty() ); + QDir testDataDir( TEST_DATA_DIR ); mTestDataPath = testDataDir.canonicalPath(); // get rid of any ".." that may cause problems later qDebug() << "test data dir:" << mTestDataPath; @@ -318,9 +324,9 @@ void TestMerginApi::testCreateProjectTwice() QCOMPARE( spy.takeFirst().at( 1 ).toBool(), true ); projects = getProjectList(); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); - QVERIFY( mCreatedProjectsModel->rowCount() ); + QVERIFY( mWorkspaceProjectsModel->rowCount() ); QVERIFY( _findProjectByName( projectNamespace, projectName, projects ).isValid() ); // Create again, expecting error @@ -380,9 +386,9 @@ void TestMerginApi::testCreateDeleteProject() QCOMPARE( spy.takeFirst().at( 1 ).toBool(), true ); projects = getProjectList(); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); - QVERIFY( mCreatedProjectsModel->rowCount() ); + QVERIFY( mWorkspaceProjectsModel->rowCount() ); Q_ASSERT( _findProjectByName( projectNamespace, projectName, projects ).isValid() ); // Delete created project @@ -562,11 +568,11 @@ void TestMerginApi::testPushAddedFile() QString projectName = "testPushAddedFile"; createRemoteProject( mApiExtra, mWorkspaceName, projectName, mTestDataPath + "/" + TEST_PROJECT_NAME + "/" ); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project0 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project0 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project0.isLocal() && project0.isMergin() ); QCOMPARE( project0.local.localVersion, 1 ); QCOMPARE( project0.mergin.serverVersion, 1 ); @@ -580,9 +586,9 @@ void TestMerginApi::testPushAddedFile() file.close(); // check that the status is "modified" - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); // force update of status + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); // force update of status - Project project1 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project1 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project1.isLocal() && project1.isMergin() ); QCOMPARE( project1.local.localVersion, 1 ); QCOMPARE( project1.mergin.serverVersion, 1 ); @@ -591,7 +597,7 @@ void TestMerginApi::testPushAddedFile() // upload uploadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project2 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project2 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project2.isLocal() && project2.isMergin() ); QCOMPARE( project2.local.localVersion, 2 ); QCOMPARE( project2.mergin.serverVersion, 2 ); @@ -601,7 +607,7 @@ void TestMerginApi::testPushAddedFile() downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project3 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project3 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project3.isLocal() && project3.isMergin() ); QCOMPARE( project3.local.localVersion, 2 ); QCOMPARE( project3.mergin.serverVersion, 2 ); @@ -620,11 +626,11 @@ void TestMerginApi::testPushRemovedFile() QString projectName = "testPushRemovedFile"; createRemoteProject( mApiExtra, mWorkspaceName, projectName, mTestDataPath + "/" + TEST_PROJECT_NAME + "/" ); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project0 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project0 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project0.isLocal() && project0.isMergin() ); QCOMPARE( project0.local.localVersion, 1 ); QCOMPARE( project0.mergin.serverVersion, 1 ); @@ -638,9 +644,9 @@ void TestMerginApi::testPushRemovedFile() QVERIFY( !file.exists() ); // check that it is considered as modified now - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); // force update of status + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); // force update of status - Project project1 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project1 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project1.isLocal() && project1.isMergin() ); QCOMPARE( project1.local.localVersion, 1 ); QCOMPARE( project1.mergin.serverVersion, 1 ); @@ -650,7 +656,7 @@ void TestMerginApi::testPushRemovedFile() uploadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project2 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project2 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project2.isLocal() && project2.isMergin() ); QCOMPARE( project2.local.localVersion, 2 ); QCOMPARE( project2.mergin.serverVersion, 2 ); @@ -660,7 +666,7 @@ void TestMerginApi::testPushRemovedFile() downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project3 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project3 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project3.isLocal() && project3.isMergin() ); QCOMPARE( project3.local.localVersion, 2 ); QCOMPARE( project3.mergin.serverVersion, 2 ); @@ -679,7 +685,7 @@ void TestMerginApi::testPushModifiedFile() QString projectName = "testPushModifiedFile"; createRemoteProject( mApiExtra, mWorkspaceName, projectName, mTestDataPath + "/" + TEST_PROJECT_NAME + "/" ); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); downloadRemoteProject( mApi, mWorkspaceName, projectName ); @@ -695,8 +701,8 @@ void TestMerginApi::testPushModifiedFile() file.close(); // check that the status is "modified" - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); // force update of status - Project project1 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); // force update of status + Project project1 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project1.isLocal() && project1.isMergin() ); QCOMPARE( project1.local.localVersion, 1 ); QCOMPARE( project1.mergin.serverVersion, 1 ); @@ -705,7 +711,7 @@ void TestMerginApi::testPushModifiedFile() // upload uploadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project2 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project2 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project2.isLocal() && project2.isMergin() ); QCOMPARE( project2.local.localVersion, 2 ); QCOMPARE( project2.mergin.serverVersion, 2 ); @@ -719,7 +725,7 @@ void TestMerginApi::testPushModifiedFile() downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project3 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project3 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project3.isLocal() && project3.isMergin() ); QCOMPARE( project3.local.localVersion, 2 ); QCOMPARE( project3.mergin.serverVersion, 2 ); @@ -736,12 +742,12 @@ void TestMerginApi::testPushNoChanges() QString projectDir = mApi->projectsPath() + "/" + projectName; createRemoteProject( mApiExtra, mWorkspaceName, projectName, mTestDataPath + "/" + TEST_PROJECT_NAME + "/" ); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); downloadRemoteProject( mApi, mWorkspaceName, projectName ); // check that the status is still "up-to-date" - Project project1 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project1 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project1.isLocal() && project1.isMergin() ); QCOMPARE( project1.local.localVersion, 1 ); QCOMPARE( project1.mergin.serverVersion, 1 ); @@ -751,7 +757,7 @@ void TestMerginApi::testPushNoChanges() uploadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project2 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project2 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project2.isLocal() && project2.isMergin() ); QCOMPARE( project2.local.localVersion, 1 ); QCOMPARE( project2.mergin.serverVersion, 1 ); @@ -771,13 +777,13 @@ void TestMerginApi::testUpdateAddedFile() QString extraProjectDir = mApiExtra->projectsPath() + "/" + projectName; createRemoteProject( mApiExtra, mWorkspaceName, projectName, mTestDataPath + "/" + TEST_PROJECT_NAME + "/" ); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); // download initial version downloadRemoteProject( mApi, mWorkspaceName, projectName ); QVERIFY( !QFile::exists( projectDir + "/test-remote-new.txt" ) ); - Project project0 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project0 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project0.isLocal() && project0.isMergin() ); QCOMPARE( project0.local.localVersion, 1 ); QCOMPARE( project0.mergin.serverVersion, 1 ); @@ -790,9 +796,9 @@ void TestMerginApi::testUpdateAddedFile() QVERIFY( QFile::exists( extraProjectDir + "/test-remote-new.txt" ) ); // list projects - just so that we can figure out we are behind - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); - Project project1 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project1 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project1.isLocal() && project1.isMergin() ); QCOMPARE( project1.local.localVersion, 1 ); QCOMPARE( project1.mergin.serverVersion, 2 ); @@ -801,7 +807,7 @@ void TestMerginApi::testUpdateAddedFile() // now try to update downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project2 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project2 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project2.isLocal() && project2.isMergin() ); QCOMPARE( project2.local.localVersion, 2 ); QCOMPARE( project2.mergin.serverVersion, 2 ); @@ -1055,7 +1061,7 @@ void TestMerginApi::testUploadWithUpdate() QString extraFilenameRemote = extraProjectDir + "/test-new-remote-file.txt"; createRemoteProject( mApiExtra, mWorkspaceName, projectName, mTestDataPath + "/" + TEST_PROJECT_NAME + "/" ); - refreshProjectsModel( ProjectsModel::CreatedProjectsModel ); + refreshProjectsModel( ProjectsModel::WorkspaceProjectsModel ); downloadRemoteProject( mApi, mWorkspaceName, projectName ); @@ -1075,7 +1081,7 @@ void TestMerginApi::testUploadWithUpdate() deleteLocalProject( mApi, mWorkspaceName, projectName ); downloadRemoteProject( mApi, mWorkspaceName, projectName ); - Project project1 = mCreatedProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); + Project project1 = mWorkspaceProjectsModel->projectFromId( MerginApi::getFullProjectName( mWorkspaceName, projectName ) ); QVERIFY( project1.isLocal() && project1.isMergin() ); QCOMPARE( project1.local.localVersion, 3 ); QCOMPARE( project1.mergin.serverVersion, 3 ); @@ -2784,10 +2790,10 @@ void TestMerginApi::refreshProjectsModel( const ProjectsModel::ProjectModelTypes QVERIFY( spy.wait( TestUtils::SHORT_REPLY ) ); QCOMPARE( spy.count(), 1 ); } - else if ( modelType == ProjectsModel::CreatedProjectsModel ) + else if ( modelType == ProjectsModel::WorkspaceProjectsModel ) { QSignalSpy spy( mApi, &MerginApi::listProjectsFinished ); - mCreatedProjectsModel->listProjects(); + mWorkspaceProjectsModel->listProjects(); QVERIFY( spy.wait( TestUtils::SHORT_REPLY ) ); QCOMPARE( spy.count(), 1 ); } diff --git a/app/test/testmerginapi.h b/app/test/testmerginapi.h index 21d14e734..77ab2fd8b 100644 --- a/app/test/testmerginapi.h +++ b/app/test/testmerginapi.h @@ -98,7 +98,7 @@ class TestMerginApi: public QObject MerginApi *mApi = nullptr; std::unique_ptr mLocalProjectsModel; - std::unique_ptr mCreatedProjectsModel; + std::unique_ptr mWorkspaceProjectsModel; std::unique_ptr mSyncManager; QString mUsername; QString mWorkspaceName; diff --git a/docs/code_convention.md b/docs/code_convention.md new file mode 100644 index 000000000..d4e18efe1 --- /dev/null +++ b/docs/code_convention.md @@ -0,0 +1,170 @@ +# Code convention + +## Cpp +We follow QGIS code style https://docs.qgis.org/3.28/en/docs/developers_guide/codingstandards.html but not that strict about documentation. + +There is a script that automatically formats your cpp code, see `scripts/format_cpp.bash` and `scripts/astyle.bash`. +Code convention for cpp is required and CI fails in case the style is violated. + +## QML +For QML we follow code style defined here: https://github.com/Furkanzmc/QML-Coding-Guide. + +One difference is that we use 2 spaces instead of 4 for indentation. +To set up correct indentation, navigate to `Preferences -> Qt Quick` in QtCreator and it set up accordingly: + +![image](https://github.com/MerginMaps/mobile/assets/22449698/3e59ae3d-6ea6-4887-ade9-c680386b47f2) + +**Unfortunatelly, there is no script that would check this convention automatically.** + +There can be only one inline `if-else` `cond ? if true : if false` in line. If you need to combine multiple of them, use normal JS `if-else` instead in multiple lines. Example: +```qml +// wrong +width: hasFocus ? 100 : isVisible ? 40 : 10 + +// good +width: { + if (hasFocus) { + return 100 + } + else if (isVisible) { + return 40 + } + return 10 +} + +height: root.visible ? 100 : 0 // one inline if-else is ok though +``` + +See full example of QML file (adjusted from https://github.com/Furkanzmc/QML-Coding-Guide?tab=readme-ov-file#full-example): +```qml +// First Qt imports +import QtQuick 2.15 +import QtQuick.Controls 2.15 +// Then custom imports +import my.library 1.0 + +Item { + id: root + + // ----- Property Declarations + + // Required properties should be at the top. + required property int radius: 0 + + property int radius: 0 + property color borderColor: "blue" + + // ----- Signal declarations + + signal clicked() + signal doubleClicked() + + // ----- In this section, we group the size and position information together. + + x: 0 + y: 0 + z: 0 + width: 100 + height: 100 + anchors.top: parent.top // If a single assignment, dot notation can be used. + // If the item is an image, sourceSize is also set here. + // sourceSize: Qt.size(12, 12) + + // ----- Then comes the other properties. There's no predefined order to these. + + // Do not use empty lines to separate the assignments. Empty lines are reserved + // for separating type declarations. + enabled: true + layer.enabled: true + + // ----- Then attached properties and attached signal handlers. + + Layout.fillWidth: true + Drag.active: false + Drag.onActiveChanged: { + + } + + // ----- States and transitions. + + states: [ + State { + + } + ] + transitions: [ + Transitions { + + } + ] + + // ----- Signal handlers + + onHeightChanged: // single-line or mutliline with brackets + + onWidthChanged: { + + } + // onCompleted and onDestruction signal handlers are always the last in + // the order. + Component.onCompleted: { + + } + Component.onDestruction: { + + } + + // ----- Visual children. + + Rectangle { + height: 50 + anchors: { // For multiple assignments, use group notation. + top: parent.top + left: parent.left + right: parent.right + } + color: "red" + layer: { + enabled: true + samples: 4 + } + } + + Rectangle { + width: parent.width + height: 1 + color: "green" + } + +// ----- Qt provided non-visual children + + Timer { + + } + + // ----- Custom non-visual children + + MyCustomNonVisualType { + + } + + QtObject { + id: privates + + property int diameter: 0 + } + + // ----- JavaScript functions + + function collapse() { + + } + + function setCollapsed(value: bool) { + if (value === true) { + } + else { + } + } +} +``` diff --git a/gallery/qml.qrc b/gallery/qml.qrc index 14840713c..63e48064a 100644 --- a/gallery/qml.qrc +++ b/gallery/qml.qrc @@ -1,6 +1,10 @@ qml/Main.qml + qml/pages/IconsPage.qml + qml/IconBox.qml + qml/pages/StylePage.qml + qml/pages/MiscPage.qml qml/pages/InitialGalleryPage.qml qml/pages/InputsPage.qml qml/pages/ButtonsPage.qml @@ -15,7 +19,6 @@ ../app/qml/components/MMTextArea.qml qml/pages/ComboBoxPage.qml ../app/qml/components/MMComboBox.qml - ../app/qml/components/MMCheckBox.qml ../app/qml/components/MMRadioButton.qml ../app/qml/components/MMSwitch.qml qml/pages/NotificationPage.qml @@ -38,5 +41,33 @@ ../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 + qml/pages/PhotosPage.qml + qml/pages/EditorsPage.qml + ../app/qml/inputs/MMAbstractEditor.qml + ../app/qml/inputs/MMCheckBox.qml + ../app/qml/inputs/MMInputEditor.qml + ../app/qml/inputs/MMPasswordEditor.qml + ../app/qml/inputs/MMSliderEditor.qml + qml/pages/OnboardingPage.qml + ../app/qml/onboarding/MMAcceptInvitation.qml + ../app/qml/onboarding/MMCreateWorkspace.qml + ../app/qml/onboarding/MMHowYouFoundUs.qml + ../app/qml/onboarding/MMLogin.qml + ../app/qml/onboarding/MMOnboarding.qml + ../app/qml/onboarding/MMSignUp.qml + ../app/qml/onboarding/MMWhichIndustry.qml + ../app/qml/components/MMHeader.qml + ../app/qml/components/MMHlineText.qml + ../app/qml/components/MMTextBubble.qml + ../app/qml/components/MMBackButton.qml + ../app/qml/components/MMIconCheckBoxHorizontal.qml + ../app/qml/components/MMIconCheckBoxVertical.qml diff --git a/gallery/qml/ColorBox.qml b/gallery/qml/ColorBox.qml new file mode 100644 index 000000000..fd9bee109 --- /dev/null +++ b/gallery/qml/ColorBox.qml @@ -0,0 +1,32 @@ +/*************************************************************************** + * * + * 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 + +Column { + spacing: 5 + id: root + + property var color + property var text + + Text { + text: root.text + } + + Rectangle { + width: 40 + height: 40 + color: root.color + border.color: "gray" + } + +} diff --git a/gallery/qml/IconBox.qml b/gallery/qml/IconBox.qml new file mode 100644 index 000000000..41450ab89 --- /dev/null +++ b/gallery/qml/IconBox.qml @@ -0,0 +1,47 @@ +/*************************************************************************** + * * + * 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 + +import "../app/qml/components" + +Column { + spacing: 5 + id: root + + required property var source + required property var text + property bool colorise: false + + Text { + text: root.text + } + + Rectangle { + width: 50 + height: 50 + border.color: "gray" + + MMIcon { + id: icon + + width: parent.width + height: parent.height + source: root.source + } + } + + Component.onCompleted: { + if (root.colorise) { + icon.color = "black" + } + } +} diff --git a/gallery/qml/Main.qml b/gallery/qml/Main.qml index 738e82116..ecabb9d3b 100644 --- a/gallery/qml/Main.qml +++ b/gallery/qml/Main.qml @@ -1,3 +1,5 @@ + + /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * @@ -6,7 +8,6 @@ * (at your option) any later version. * * * ***************************************************************************/ - import QtCore import QtQuick import QtQuick.Layouts @@ -90,7 +91,7 @@ ApplicationWindow { onClicked: { window.currentPageSource = model.source listView.currentIndex = index - if( __isMobile ) + if (__isMobile) stackView.push("qrc:/qml/pages/" + model.source) else stackView.push("file://" + _qmlWrapperPath + model.source) @@ -104,10 +105,22 @@ ApplicationWindow { title: "Initial" source: "InitialGalleryPage.qml" } + ListElement { + title: "Style" + source: "StylePage.qml" + } + ListElement { + title: "Icons" + source: "IconsPage.qml" + } ListElement { title: "Buttons" source: "ButtonsPage.qml" } + ListElement { + title: "Editors" + source: "EditorsPage.qml" + } ListElement { title: "Inputs" source: "InputsPage.qml" @@ -132,6 +145,10 @@ ApplicationWindow { title: "Drawers" source: "DrawerPage.qml" } + ListElement { + title: "Photos" + source: "PhotosPage.qml" + } ListElement { title: "Map" source: "MapPage.qml" @@ -140,6 +157,18 @@ ApplicationWindow { title: "Toolbars" source: "ToolbarPage.qml" } + ListElement { + title: "Project items" + source: "ProjectItemsPage.qml" + } + ListElement { + title: "Misc" + source: "MiscPage.qml" + } + ListElement { + title: "Onboarding" + source: "OnboardingPage.qml" + } } ScrollIndicator.vertical: ScrollIndicator {} @@ -152,7 +181,7 @@ ApplicationWindow { initialItem: Loader { id: mainLoader - source: ( __isMobile ? "qrc:/qml/pages/" : ("file://" + _qmlWrapperPath ) ) + currentPageSource + source: (__isMobile ? "qrc:/qml/pages/" : ("file://" + _qmlWrapperPath)) + currentPageSource scale: 1.0 } } diff --git a/gallery/qml/pages/ButtonsPage.qml b/gallery/qml/pages/ButtonsPage.qml index 81debb1eb..ab53d4e15 100644 --- a/gallery/qml/pages/ButtonsPage.qml +++ b/gallery/qml/pages/ButtonsPage.qml @@ -15,7 +15,7 @@ import "../../app/qml/components" Column { padding: 20 - spacing: 20 + spacing: 5 GroupBox { title: "MMButton" @@ -86,10 +86,14 @@ Column { anchors.fill: parent MMLink { text: "Tertriary" + width: 150 + rightIcon: __style.arrowLinkRightIcon onClicked: text = (text === "Clicked" ? "Tertriary" : "Clicked") } MMLink { text: "Disabled" + rightIcon: __style.arrowLinkRightIcon + width: 150 enabled: false } } @@ -140,4 +144,26 @@ Column { } } } + + GroupBox { + title: "MMBackButton" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Row { + spacing: 10 + anchors.fill: parent + + MMBackButton { + enabled: false + } + } + } } diff --git a/gallery/qml/pages/ChecksPage.qml b/gallery/qml/pages/ChecksPage.qml index 6c1ce2993..6b20522b4 100644 --- a/gallery/qml/pages/ChecksPage.qml +++ b/gallery/qml/pages/ChecksPage.qml @@ -12,6 +12,7 @@ import QtQuick.Controls import QtQuick.Controls.Basic import "../../app/qml/components" +import "../../app/qml/inputs" Column { padding: 20 @@ -19,6 +20,7 @@ Column { GroupBox { title: "MMCheckBox" + width: 200 background: Rectangle { color: "lightGray" border.color: "gray" @@ -29,7 +31,7 @@ Column { padding: 5 } - Row { + Column { spacing: 20 anchors.fill: parent MMCheckBox { @@ -50,6 +52,82 @@ Column { } } + GroupBox { + title: "MMIconCheckBoxHorizontal" + background: Rectangle { + color: __style.lightGreenColor + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Row { + spacing: 10 + anchors.fill: parent + + MMIconCheckBoxHorizontal { + checked: false + sourceIcon: __style.qgisIcon + text: "QGIS website" + } + + MMIconCheckBoxHorizontal { + checked: true + sourceIcon: __style.qgisIcon + text: "QGIS website" + } + + MMIconCheckBoxHorizontal { + checked: false + sourceIcon: __style.redditIcon + text: "Reddit" + small: true + } + + MMIconCheckBoxHorizontal { + checked: true + sourceIcon: __style.redditIcon + text: "Reddit" + small: true + } + } + } + + GroupBox { + title: "MMIconCheckBoxVertical" + background: Rectangle { + color: __style.lightGreenColor + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Row { + spacing: 10 + anchors.fill: parent + + MMIconCheckBoxVertical { + checked: false + sourceIcon: __style.archaeologyIcon + text: "Archeology" + bgColorIcon: __style.sandColor + } + + MMIconCheckBoxVertical { + checked: true + sourceIcon: __style.tractorIcon + text: "Agriculture Long text" + bgColorIcon: __style.sunColor + } + } + } + GroupBox { title: "MMRadioButton" background: Rectangle { diff --git a/gallery/qml/pages/DrawerPage.qml b/gallery/qml/pages/DrawerPage.qml index 8dec758a9..c0f31ea92 100644 --- a/gallery/qml/pages/DrawerPage.qml +++ b/gallery/qml/pages/DrawerPage.qml @@ -39,7 +39,7 @@ Page { id: drawer1 picture: __style.uploadImage - title: "Upload project to Margin?" + bigTitle: "Upload project to Margin?" description: "This project is currently not uploaded on Mergin. Upload it to Mergin in order to activate synchronization and collaboration." primaryButton: "Yes, Upload Project" secondaryButton: "No Cancel" @@ -52,7 +52,7 @@ Page { id: drawer2 picture: __style.reachedDataLimitImage - title: "You have reached a data limit" + bigTitle: "You have reached a data limit" primaryButton: "Manage Subscription" specialComponent: component.comp visible: true @@ -73,7 +73,7 @@ Page { id: drawer3 picture: __style.uploadImage - title: "Failed to synchronize your changes" + bigTitle: "Failed to synchronize your changes" description: "Your changes could not be sent to server, make sure you are connected to internet and have write access to this project." primaryButton: "Ok, I understand" boundedDescription: "Failed to push changes. Ask the project workspace owner to log in to their Mergin Maps dashboard for more information." diff --git a/gallery/qml/pages/EditorsPage.qml b/gallery/qml/pages/EditorsPage.qml new file mode 100644 index 000000000..fcc8fcfef --- /dev/null +++ b/gallery/qml/pages/EditorsPage.qml @@ -0,0 +1,85 @@ +/*************************************************************************** + * * + * 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 + +import "../../app/qml/inputs" + +ScrollView { + Column { + padding: 20 + spacing: 20 + + GroupBox { + title: "Items based on MMAbstractEditor" + background: Rectangle { + color: "lightGray" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 10 + width: 300 + + MMCheckBox { + id: checkbox + text: checked ? "enabled" : "disabled" + checked: true + } + + MMSliderEditor { + title: "MMSliderEditor" + from: -100 + to: 100 + parentValue: -100 + suffix: " s" + width: parent.width + enabled: checkbox.checked + onEditorValueChanged: function(newValue) { errorMsg = newValue > 0 ? "" : "Set positive value!" } + hasCheckbox: true + checkboxChecked: true + } + + MMInputEditor { + title: "MMInputEditor" + parentValue: "Text" + enabled: checkbox.checked + width: parent.width + hasCheckbox: true + checkboxChecked: false + } + + MMInputEditor { + title: "MMInputEditor" + placeholderText: "Placeholder" + enabled: checkbox.checked + width: parent.width + warningMsg: text.length > 0 ? "" : "Write something" + } + + MMPasswordEditor { + title: "MMPasswordEditor" + parentValue: "Password" + //regexp: '(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{6,})' + errorMsg: "Password must contain at least 6 characters\nMinimum 1 number, uppercase and lowercase letter and special character" + enabled: checkbox.checked + width: parent.width + } + } + } + + } +} diff --git a/gallery/qml/pages/IconsPage.qml b/gallery/qml/pages/IconsPage.qml new file mode 100644 index 000000000..5cc60455a --- /dev/null +++ b/gallery/qml/pages/IconsPage.qml @@ -0,0 +1,315 @@ +/*************************************************************************** + * * + * 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 + +import "../../app/qml/components" +import "../" + +ScrollView { + Column { + padding: 20 + spacing: 5 + + property int rectSize: 10 + + GroupBox { + title: "Icons" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 3 + spacing: 20 + anchors.fill: parent + IconBox { + text: "arrowDownIcon" + source: __style.arrowDownIcon + } + IconBox { + text: "arrowLinkRightIcon" + source: __style.arrowLinkRightIcon + colorise: true + } + IconBox { + text: "arrowUpIcon" + source: __style.arrowUpIcon + } + IconBox { + text: "backIcon" + source: __style.backIcon + } + IconBox { + text: "calendarIcon" + source: __style.calendarIcon + } + IconBox { + text: "checkmarkIcon" + source: __style.checkmarkIcon + } + IconBox { + text: "closeButtonIcon" + source: __style.closeButtonIcon + } + IconBox { + text: "closeIcon" + source: __style.closeIcon + colorise: true + } + IconBox { + text: "deleteIcon" + source: __style.deleteIcon + colorise: true + } + IconBox { + text: "doneIcon" + source: __style.doneIcon + } + IconBox { + text: "downloadIcon" + source: __style.downloadIcon + } + IconBox { + text: "editIcon" + source: __style.editIcon + colorise: true + } + IconBox { + text: "errorIcon" + source: __style.errorIcon + } + IconBox { + text: "hideIcon" + source: __style.hideIcon + } + IconBox { + text: "infoIcon" + source: __style.infoIcon + } + IconBox { + text: "moreIcon" + source: __style.moreIcon + colorise: true + } + IconBox { + text: "morePhotosIcon" + source: __style.morePhotosIcon + colorise: true + } + IconBox { + text: "projectButtonMoreIcon" + source: __style.projectButtonMoreIcon + } + IconBox { + text: "qrCodeIcon" + source: __style.qrCodeIcon + } + IconBox { + text: "searchIcon" + source: __style.searchIcon + } + IconBox { + text: "showIcon" + source: __style.showIcon + } + IconBox { + text: "stopIcon" + source: __style.stopIcon + colorise: true + } + IconBox { + text: "syncIcon" + source: __style.syncIcon + } + IconBox { + text: "waitingIcon" + source: __style.waitingIcon + } + IconBox { + text: "xMarkIcon" + source: __style.xMarkIcon + } + IconBox { + text: "globeIcon" + source: __style.globeIcon + } + } + } + + GroupBox { + title: "Icons - How you found us" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 3 + spacing: 20 + anchors.fill: parent + IconBox { + text: "searchIcon" + source: __style.searchIcon + } + IconBox { + text: "termsIcon" + source: __style.termsIcon + } + IconBox { + text: "mouthIcon" + source: __style.mouthIcon + } + IconBox { + text: "qgisIcon" + source: __style.qgisIcon + } + IconBox { + text: "subscriptionsIcon" + source: __style.subscriptionsIcon + } + IconBox { + text: "teacherIcon" + source: __style.teacherIcon + } + IconBox { + text: "briefcaseIcon" + source: __style.briefcaseIcon + } + IconBox { + text: "socialMediaIcon" + source: __style.socialMediaIcon + } + IconBox { + text: "otherIcon" + source: __style.otherIcon + } + } + } + + GroupBox { + title: "Icons - Social" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 3 + spacing: 20 + anchors.fill: parent + IconBox { + text: "youtubeIcon" + source: __style.youtubeIcon + } + IconBox { + text: "xTwitterIcon" + source: __style.xTwitterIcon + } + IconBox { + text: "facebookIcon" + source: __style.facebookIcon + } + IconBox { + text: "linkedinIcon" + source: __style.linkedinIcon + } + IconBox { + text: "mastodonIcon" + source: __style.mastodonIcon + } + IconBox { + text: "redditIcon" + source: __style.redditIcon + } + } + } + + GroupBox { + title: "Icons - Industries" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 2 + spacing: 20 + anchors.fill: parent + IconBox { + text: "tractorIcon" + source: __style.tractorIcon + } + IconBox { + text: "archaeologyIcon" + source: __style.archaeologyIcon + } + IconBox { + text: "engineeringIcon" + source: __style.engineeringIcon + } + IconBox { + text: "electricityIcon" + source: __style.electricityIcon + } + IconBox { + text: "environmentalIcon" + source: __style.environmentalIcon + } + IconBox { + text: "stateAndLocalIcon" + source: __style.stateAndLocalIcon + } + IconBox { + text: "naturalResourcesIcon" + source: __style.naturalResourcesIcon + } + IconBox { + text: "telecommunicationIcon" + source: __style.telecommunicationIcon + } + IconBox { + text: "transportationIcon" + source: __style.transportationIcon + } + IconBox { + text: "waterResourcesIcon" + source: __style.waterResourcesIcon + } + IconBox { + text: "othersIcon" + source: __style.othersIcon + } + } + } + } +} diff --git a/gallery/qml/pages/MiscPage.qml b/gallery/qml/pages/MiscPage.qml new file mode 100644 index 000000000..25a708561 --- /dev/null +++ b/gallery/qml/pages/MiscPage.qml @@ -0,0 +1,200 @@ +/*************************************************************************** + * * + * 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 + +import "../../app/qml/components" + +ScrollView { + id: page + + Column { + id: mainColumn + + x: 20 + width: page.width + spacing: 5 + + Row { + spacing: 5 + GroupBox { + title: "MMProgressBar" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 20 + anchors.fill: parent + MMProgressBar { + position: 0 + } + MMProgressBar { + position: 0.6 + } + } + } + + GroupBox { + title: "MMProgressBar" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 20 + anchors.fill: parent + MMProgressBar { + width: 60 * __dp + height: 4 * __dp + + position: 0 + color: __style.grassColor + progressColor: __style.forestColor + } + MMProgressBar { + width: 60 * __dp + height: 4 * __dp + + position: 0.6 + color: __style.grassColor + progressColor: __style.forestColor + } + } + } + } + + GroupBox { + title: "MMHeader" + width: page.width - 40 + + background: Rectangle { + color: __style.lightGreenColor + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 20 + anchors.fill: parent + + + MMHeader { + headerTitle: "Only title" + backVisible: false + step: -1 + } + + MMHeader { + headerTitle: "Title with back button" + step: 0 + } + + MMHeader { + headerTitle: "Title with progress bar" + backVisible: false + step: 1 + } + + MMHeader { + headerTitle: "Title with back button and Progress bar" + step: 2 + } + } + } + + GroupBox { + title: "MMHeader (bigger title)" + width: page.width - 40 + + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 20 + anchors.fill: parent + + MMHeader { + headerTitle: "Drawer title" + titleFont: __style.h3 + backColor: __style.lightGreenColor + } + } + } + + GroupBox { + title: "MMHlineText" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 20 + anchors.fill: parent + MMHlineText { + width: page.width - 64 + title: "My text is great" + } + } + } + + GroupBox { + title: "MMTextBubble" + background: Rectangle { + color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + + Column { + spacing: 20 + anchors.fill: parent + MMTextBubble { + width: page.width - 64 + title: "Tip from Mergin Maps" + description: "A good candidate for a workspace name is the name of your team or organisation" + } + } + } + } +} diff --git a/gallery/qml/pages/OnboardingPage.qml b/gallery/qml/pages/OnboardingPage.qml new file mode 100644 index 000000000..8e9309035 --- /dev/null +++ b/gallery/qml/pages/OnboardingPage.qml @@ -0,0 +1,151 @@ +/*************************************************************************** + * * + * 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.Layouts + +import "../../app/qml/onboarding" + +Page { + id: pane + + function hideAll() { + acceptInvitation.visible = false + createWorkspace = false + howYouFoundUs = false + login = false + signUp = false + whichIndustry = false + } + + Column { + id: layout + padding: 20 + spacing: 10 + + Text { + text: "Onboarding (Login, Sign Up, ...)" + color: "green" + } + + Button { + onClicked: { + login.visible = true + } + text: "Login" + } + + Button { + onClicked: { + signUp.visible = true + } + text: "Sign Up" + } + + Button { + onClicked: { + acceptInvitation.visible = true + } + text: "Accept Invitation" + } + + Button { + onClicked: { + createWorkspace.visible = true + } + text: "Create Workspace" + } + + Button { + onClicked: { + howYouFoundUs.visible = true + } + text: "How You Found Us" + } + + Button { + onClicked: { + whichIndustry.visible = true + } + text: "Which Industry" + } + } + + MMLogin { + id: login + + anchors.fill: parent + visible: false + + onSignInClicked: console.log("Sign in clicked") + onSignUpClicked: console.log("Sign up clicked") + onChangeServerClicked: console.log("Change server clicked") + onBackClicked: visible = false + } + + MMSignUp { + id: signUp + + anchors.fill: parent + visible: false + + onSignInClicked: console.log("Sign in clicked") + onSignUpClicked: console.log("Sign up clicked") + onBackClicked: visible = false + } + + MMAcceptInvitation { + id: acceptInvitation + + anchors.fill: parent + visible: false + user: "Lubos" + workspace: "my-workspace.funny" + + onBackClicked: visible = false + onContinueClicked: console.log("Join workspace clicked") + onCreateWorkspaceClicked: console.log("Create new workspace clicked") + } + + MMCreateWorkspace { + id: createWorkspace + + anchors.fill: parent + visible: false + + onContinueClicked: visible = false + } + + MMHowYouFoundUs { + id: howYouFoundUs + + anchors.fill: parent + visible: false + + onBackClicked: visible = false + onContinueClicked: function(selectedText) { + console.log("Selected: " + selectedText) + visible = false + } + } + + MMWhichIndustry { + id: whichIndustry + + anchors.fill: parent + visible: false + + onBackClicked: visible = false + onContinueClicked: function(selectedText) { + console.log("Selected: " + selectedText) + visible = false + } + } +} diff --git a/gallery/qml/pages/PhotosPage.qml b/gallery/qml/pages/PhotosPage.qml new file mode 100644 index 000000000..647cf0ec5 --- /dev/null +++ b/gallery/qml/pages/PhotosPage.qml @@ -0,0 +1,71 @@ +/*************************************************************************** + * * + * 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 + +import "../../app/qml/components" + +Page { + + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + Column { + width: parent.width + + MMPhotoGallery { + title: "Gallery 7 photos - show just 5" + maxVisiblePhotos: 5 + model: [ + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + ] + onShowAll: console.log("Open Gallery") + onClicked: function( path ) { console.log("Open " + path) } + } + + MMPhotoGallery { + title: "Gallery 7 photos - show all" + maxVisiblePhotos: -1 + model: [ + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + ] + onShowAll: console.log("Open Gallery") + onClicked: function( path ) { console.log("Open " + path) } + } + + MMPhotoGallery { + title: "Gallery 3 photos" + warningMsg: "The size of image is too big" + model: [ + "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg", + "https://images.pexels.com/photos/955656/pexels-photo-955656.jpeg", + "https://images.pexels.com/photos/559422/pexels-photo-559422.jpeg" + ] + onShowAll: console.log("Open Gallery") + onClicked: function( path ) { console.log("Open " + path) } + } + } +} 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") + } + + } +} diff --git a/gallery/qml/pages/StylePage.qml b/gallery/qml/pages/StylePage.qml new file mode 100644 index 000000000..9ba3efad1 --- /dev/null +++ b/gallery/qml/pages/StylePage.qml @@ -0,0 +1,308 @@ +/*************************************************************************** + * * + * 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 + +import "../../app/qml/components" +import "../" + +ScrollView { + Column { + id: page + + padding: 20 + spacing: 5 + + property int rectSize: 10 + + GroupBox { + title: "Fonts" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 7 + spacing: 20 + anchors.fill: parent + Text { + text: "h1" + font: __style.h1 + } + Text { + text: "h2" + font: __style.h2 + } + Text { + text: "h3" + font: __style.h3 + } + Item { + width: 1 + height: 1 + } + Item { + width: 1 + height: 1 + } + Item { + width: 1 + height: 1 + } + Item { + width: 1 + height: 1 + } + Text { + text: "t1" + font: __style.t1 + } + Text { + text: "t2" + font: __style.t2 + } + Text { + text: "t3" + font: __style.t3 + } + Text { + text: "t4" + font: __style.t4 + } + Text { + text: "t5" + font: __style.t5 + } + Item { + width: 1 + height: 1 + } + Item { + width: 1 + height: 1 + } + Text { + text: "p1" + font: __style.p1 + } + Text { + text: "p2" + font: __style.p2 + } + Text { + text: "p3" + font: __style.p3 + } + Text { + text: "p4" + font: __style.p4 + } + Text { + text: "p5" + font: __style.p5 + } + Text { + text: "p6" + font: __style.p6 + } + Text { + text: "p7" + font: __style.p7 + } + } + + } + + GroupBox { + title: "Colors - primary palette" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + + Grid { + columns: 3 + spacing: 20 + anchors.fill: parent + ColorBox { + text: "grassColor" + color: __style.grassColor + } + ColorBox { + text: "forestColor" + color: __style.forestColor + } + ColorBox { + text: "nightColor" + color: __style.nightColor + } + ColorBox { + text: "whiteColor" + color: __style.whiteColor + } + ColorBox { + text: "transparentColor" + color: __style.transparentColor + } + } + } + + GroupBox { + title: "Colors - additional colors" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 3 + spacing: 20 + anchors.fill: parent + ColorBox { + text: "sandColor" + color: __style.sandColor + } + ColorBox { + text: "sunsetColor" + color: __style.sunsetColor + } + ColorBox { + text: "sunColor" + color: __style.sunColor + } + ColorBox { + text: "earthColor" + color: __style.earthColor + } + ColorBox { + text: "roseColor" + color: __style.roseColor + } + ColorBox { + text: "skyColor" + color: __style.skyColor + } + ColorBox { + text: "grapeColor" + color: __style.grapeColor + } + ColorBox { + text: "deepOceanColor" + color: __style.deepOceanColor + } + ColorBox { + text: "purpleColor" + color: __style.purpleColor + } + ColorBox { + text: "fieldColor" + color: __style.fieldColor + } + } + + } + + GroupBox { + title: "Colors - additional colors" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 2 + spacing: 20 + anchors.fill: parent + ColorBox { + text: "positiveColor" + color: __style.positiveColor + } + ColorBox { + text: "warningColor" + color: __style.warningColor + } + ColorBox { + text: "negativeColor" + color: __style.negativeColor + } + ColorBox { + text: "informativeColor" + color: __style.informativeColor + } + } + } + + GroupBox { + title: "Colors - others" + background: Rectangle { + color: "white" + border.color: "gray" + } + label: Text { + color: "black" + text: parent.title + padding: 5 + } + + Grid { + columns: 3 + spacing: 20 + anchors.fill: parent + ColorBox { + text: "nightAlphaColor" + color: __style.nightAlphaColor + } + ColorBox { + text: "errorBgInputColor" + color: __style.errorBgInputColor + } + ColorBox { + text: "shadowColor" + color: __style.shadowColor + } + ColorBox { + text: "lightGreenColor" + color: __style.lightGreenColor + } + ColorBox { + text: "mediumGreenColor" + color: __style.mediumGreenColor + } + ColorBox { + text: "grayColor" + color: __style.grayColor + } + } + } + } +}