diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b2f30ed01..03c894f1b 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -48,6 +48,7 @@ set(MM_SRCS autosynccontroller.cpp bluetoothdiscoverymodel.cpp qrcodedecoder.cpp + changelogmodel.cpp compass.cpp featurelayerpair.cpp featuresmodel.cpp @@ -127,6 +128,7 @@ set(MM_HDRS autosynccontroller.h bluetoothdiscoverymodel.h qrcodedecoder.h + changelogmodel.h compass.h enumhelper.h featurelayerpair.h diff --git a/app/changelogmodel.cpp b/app/changelogmodel.cpp new file mode 100644 index 000000000..8520fbe61 --- /dev/null +++ b/app/changelogmodel.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "changelogmodel.h" +#include +#include +#include +#include "coreutils.h" +#include "inputhelp.h" + +ChangelogModel::ChangelogModel( QObject *parent ) : QAbstractListModel{parent} +{ + mNetworkManager = new QNetworkAccessManager( this ); + connect( mNetworkManager, &QNetworkAccessManager::finished, this, &ChangelogModel::onFinished ); +} + +void ChangelogModel::onFinished( QNetworkReply *reply ) +{ + if ( reply->error() == QNetworkReply::NoError ) + { + QXmlStreamReader xml( reply ); + QString title, description, link, pubDate; + while ( !xml.atEnd() ) + { + xml.readNext(); + if ( xml.isStartElement() ) + { + if ( xml.name().toString() == QStringLiteral( "item" ) ) + { + title.clear(); + description.clear(); + link.clear(); + pubDate.clear(); + } + else if ( xml.name().toString() == QStringLiteral( "title" ) ) + { + title = xml.readElementText(); + } + else if ( xml.name().toString() == QStringLiteral( "description" ) ) + { + description = xml.readElementText(); + } + else if ( xml.name().toString() == QStringLiteral( "link" ) ) + { + link = xml.readElementText(); + } + else if ( xml.name().toString() == QStringLiteral( "pubDate" ) ) + { + pubDate = xml.readElementText(); + } + } + else if ( xml.isEndElement() ) + { + if ( xml.name().toString() == "item" ) + { + const QDateTime &dt = QDateTime::fromString( pubDate, "ddd, dd MMM yyyy hh:mm:ss t" ); + beginInsertRows( QModelIndex(), rowCount(), rowCount() ); + mLogs << Changelog{ title, description, link, dt }; + endInsertRows(); + } + } + } + if ( xml.hasError() ) + { + CoreUtils::log( QStringLiteral( "Changelog" ), QStringLiteral( "Failed to parse changelog. Xml parse error: " ).arg( xml.errorString() ) ); + } + } + else + { + CoreUtils::log( QStringLiteral( "Changelog" ), QStringLiteral( "Failed to get changelog. Server Error: %1" ).arg( reply->errorString() ) ); + emit errorMsgChanged( reply->errorString() ); + } + reply->deleteLater(); + + if ( !mLogs.isEmpty() ) + { + emit dataChanged( createIndex( 0, 0 ), createIndex( rowCount(), 0 ) ); + } +} + +QHash ChangelogModel::roleNames() const +{ + return + { + { TitleRole, "title" }, + { DescriptionRole, "description" }, + { LinkRole, "link" }, + { DateRole, "date" } + }; +} + +int ChangelogModel::rowCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return mLogs.size(); +} + +QVariant ChangelogModel::data( const QModelIndex &index, int role ) const +{ + if ( !hasIndex( index.row(), index.column(), index.parent() ) ) + return {}; + + Changelog log = mLogs.at( index.row() ); + if ( role == TitleRole ) return log.title; + if ( role == DescriptionRole ) return log.descriptionWithoutImages(); + if ( role == LinkRole ) return log.link; + if ( role == DateRole ) return log.date; + + return {}; +} + +// fill the dialog +void ChangelogModel::seeChangelogs() +{ + beginResetModel(); + mLogs.clear(); + endResetModel(); + mNetworkManager->get( QNetworkRequest( QUrl( InputHelp::changelogLink() ) ) ); +} diff --git a/app/changelogmodel.h b/app/changelogmodel.h new file mode 100644 index 000000000..d09638d1d --- /dev/null +++ b/app/changelogmodel.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef CHANGELOGMODEL_H +#define CHANGELOGMODEL_H + +#include "QNetworkAccessManager" +#include +#include +#include + +struct Changelog +{ + QString title; + QString description; + QString link; + QDateTime date; + + QString descriptionWithoutImages() { return description.replace( QRegularExpression( "" ), "" ); }; +}; + +class ChangelogModel : public QAbstractListModel +{ + Q_OBJECT + Q_ENUMS( MyRoles ) + + public: + enum MyRoles + { + TitleRole = Qt::UserRole + 1, DescriptionRole, LinkRole, DateRole + }; + + ChangelogModel( QObject *parent = nullptr ); + + QHash roleNames() const override; + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + + Q_INVOKABLE void seeChangelogs(); + + private slots: + void onFinished( QNetworkReply *reply ); + + signals: + void finished( const QString &title, const QString &link ); + void errorMsgChanged( const QString &msg ); + + private: + QList mLogs; + QNetworkAccessManager *mNetworkManager; +}; + +#endif // CHANGELOGMODEL_H diff --git a/app/icons/Close.svg b/app/icons/Close.svg new file mode 100644 index 000000000..e33397dfc --- /dev/null +++ b/app/icons/Close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/Delete.svg b/app/icons/Delete.svg new file mode 100644 index 000000000..ad13d3664 --- /dev/null +++ b/app/icons/Delete.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Done.svg b/app/icons/Done.svg new file mode 100644 index 000000000..16b8b2190 --- /dev/null +++ b/app/icons/Done.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/icons/Edit.svg b/app/icons/Edit.svg new file mode 100644 index 000000000..0bde4f126 --- /dev/null +++ b/app/icons/Edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/icons/More.svg b/app/icons/More.svg new file mode 100644 index 000000000..2829355ba --- /dev/null +++ b/app/icons/More.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/icons/Waiting.svg b/app/icons/Waiting.svg new file mode 100644 index 000000000..d435640ec --- /dev/null +++ b/app/icons/Waiting.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/icons/icons.qrc b/app/icons/icons.qrc index 7f82ade42..832b11c2e 100644 --- a/app/icons/icons.qrc +++ b/app/icons/icons.qrc @@ -14,5 +14,11 @@ CloseButton.svg UploadImage.svg ReachedDataLimitImage.svg + Close.svg + Waiting.svg + Delete.svg + Done.svg + Edit.svg + More.svg diff --git a/app/inputhelp.cpp b/app/inputhelp.cpp index 5589b9028..14bff295e 100644 --- a/app/inputhelp.cpp +++ b/app/inputhelp.cpp @@ -25,6 +25,7 @@ const QString helpRoot = QStringLiteral( "https://merginmaps.com/docs" ); const QString reportLogUrl = QStringLiteral( "https://g4pfq226j0.execute-api.eu-west-1.amazonaws.com/mergin_client_log_submit" ); const QString helpDeskMail = QStringLiteral( "support@merginmaps.com" ); const QString inputWeb = QStringLiteral( "https://merginmaps.com" ); +const QString changelogRss = QStringLiteral( "https://wishlist.merginmaps.com/rss/changelog.xml" ); const QString utmTagHelp = QStringLiteral( "?utm_source=input-help&utm_medium=help&utm_campaign=input" ); const QString utmTagSubscription = QStringLiteral( "?utm_source=input-subs&utm_medium=subs&utm_campaign=input" ); @@ -164,6 +165,11 @@ QString InputHelp::whatsNewPostLink() const return inputWeb + "/blog/introducing-workspaces-simplified-collaboration" + utmTagOther; } +QString InputHelp::changelogLink() +{ + return changelogRss; +} + QString InputHelp::migrationGuides() const { return helpRoot + "/dev/ce-migration/" + utmTagHelp; @@ -218,6 +224,7 @@ QVector InputHelp::logHeader( bool isHtml ) { QVector retLines; retLines.push_back( QStringLiteral( "Input App: %1 - %2 (%3)" ).arg( CoreUtils::appVersion() ).arg( InputUtils::appPlatform() ).arg( CoreUtils::appVersionCode() ) ); + retLines.push_back( QStringLiteral( "Data Dir: %1" ).arg( InputUtils::appDataDir() ) ); retLines.push_back( QStringLiteral( "System: %1" ).arg( QSysInfo::prettyProductName() ) ); retLines.push_back( QStringLiteral( "Mergin URL: %1" ).arg( mMerginApi->apiRoot() ) ); retLines.push_back( QStringLiteral( "Mergin User: %1" ).arg( mMerginApi->userAuth()->username() ) ); diff --git a/app/inputhelp.h b/app/inputhelp.h index 9e0f1f24b..77114440e 100644 --- a/app/inputhelp.h +++ b/app/inputhelp.h @@ -74,6 +74,7 @@ class InputHelp: public QObject QString merginTermsLink() const; QString projectLoadingErrorHelpLink() const; QString whatsNewPostLink() const; + static QString changelogLink(); QString migrationGuides() const; bool submitReportPending() const; diff --git a/app/inpututils.cpp b/app/inpututils.cpp index bc0f17f3b..b2b0ffbdf 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -614,6 +614,12 @@ bool InputUtils::isMobilePlatform() return platform == QStringLiteral( "android" ) || platform == QStringLiteral( "ios" ); } +QString InputUtils::appDataDir() +{ + QString dataDir = QString::fromLocal8Bit( qgetenv( "QGIS_QUICK_DATA_PATH" ) ) ; + return dataDir; +} + void InputUtils::onQgsLogMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level ) { QString levelStr; diff --git a/app/inpututils.h b/app/inpututils.h index 27e95b3ed..d9bfda744 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -169,6 +169,8 @@ class InputUtils: public QObject static QString appPlatform(); static bool isMobilePlatform(); + static QString appDataDir(); + /** * Converts string in rational number format to double. * @param rationalValue String - expecting value in format "numerator/denominator" (e.g "123/100"). diff --git a/app/main.cpp b/app/main.cpp index f9fc34149..af0fb5e2a 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -126,6 +126,7 @@ #include "workspacesproxymodel.h" #include "invitationsmodel.h" #include "invitationsproxymodel.h" +#include "changelogmodel.h" #include "streamingintervaltype.h" @@ -250,6 +251,16 @@ static void init_qgis( const QString &pkgPath ) qDebug( "qgis providers:\n%s", QgsProviderRegistry::instance()->pluginList().toLatin1().data() ); } +static void init_pg( const QString &dataDir ) +{ + QFileInfo pgFile( QStringLiteral( "%1/pg_service.conf" ).arg( dataDir ) ); + if ( pgFile.exists() && pgFile.isReadable() ) + { + qputenv( "PGSYSCONFDIR", dataDir.toUtf8() ); + CoreUtils::log( QStringLiteral( "PostgreSQL" ), QStringLiteral( "found pg_service.conf, setting PGSYSCONFDIR" ) ); + } +} + void initDeclarative() { qmlRegisterUncreatableType( "lc", 1, 0, "MerginUserAuth", "" ); @@ -279,6 +290,7 @@ void initDeclarative() qmlRegisterType( "lc", 1, 0, "WorkspacesProxyModel" ); qmlRegisterType( "lc", 1, 0, "InvitationsModel" ); qmlRegisterType( "lc", 1, 0, "InvitationsProxyModel" ); + qmlRegisterType( "lc", 1, 0, "ChangelogModel" ); qmlRegisterUncreatableType( "lc", 1, 0, "AttributePreviewModel", "" ); qmlRegisterUncreatableMetaObject( ProjectStatus::staticMetaObject, "lc", 1, 0, "ProjectStatus", "ProjectStatus Enum" ); qRegisterMetaType< FeatureLayerPair >( "FeatureLayerPair" ); @@ -393,7 +405,7 @@ int main( int argc, char *argv[] ) tests.parseArgs( argc, argv ); #endif qDebug() << "Mergin Maps Input App" << version << InputUtils::appPlatform() << "(" << CoreUtils::appVersionCode() << ")"; - qDebug() << "Built with QGIS version " << VERSION_INT; + qDebug() << "Built with QGIS " << VERSION_INT << " and QT " << qVersion(); // Set/Get enviroment QString dataDir = getDataDir(); @@ -413,6 +425,8 @@ int main( int argc, char *argv[] ) } CoreUtils::setLogFilename( projectDir + "/.logs" ); + CoreUtils::log( QStringLiteral( "Input" ), QStringLiteral( "Application has started: %1 (%2)" ).arg( version ).arg( CoreUtils::appVersionCode() ) ); + setEnvironmentQgisPrefixPath(); // Initialize translations @@ -451,6 +465,9 @@ int main( int argc, char *argv[] ) #endif InputProjUtils inputProjUtils; inputProjUtils.initProjLib( appBundleDir, dataDir, projectDir ); + + init_pg( dataDir ); + init_qgis( appBundleDir ); // AppSettings has to be initialized after QGIS app init (because of correct reading/writing QSettings). diff --git a/app/mmstyle.h b/app/mmstyle.h new file mode 100644 index 000000000..1292127c5 --- /dev/null +++ b/app/mmstyle.h @@ -0,0 +1,201 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef MMSTYLE_H +#define MMSTYLE_H + +#include +#include +#include +#include + +class MMStyle: public QObject +{ + Q_OBJECT + + // Fonts - how to use + // standard - font: __style.p5 + // extended - font.pixelSize: __style.p5.pixelSize; font.italic: true + + // Fonts - Title + Q_PROPERTY( QFont t1 READ t1 CONSTANT ) + Q_PROPERTY( QFont t2 READ t2 CONSTANT ) + Q_PROPERTY( QFont t3 READ t3 CONSTANT ) + Q_PROPERTY( QFont t4 READ t4 CONSTANT ) + Q_PROPERTY( QFont t5 READ t5 CONSTANT ) + + // Fonts - Paragraph + Q_PROPERTY( QFont p1 READ p1 CONSTANT ) + Q_PROPERTY( QFont p2 READ p2 CONSTANT ) + Q_PROPERTY( QFont p3 READ p3 CONSTANT ) + Q_PROPERTY( QFont p4 READ p4 CONSTANT ) + Q_PROPERTY( QFont p5 READ p5 CONSTANT ) + Q_PROPERTY( QFont p6 READ p6 CONSTANT ) + Q_PROPERTY( QFont p7 READ p7 CONSTANT ) + + // Colors - primary palette + Q_PROPERTY( QColor grassColor READ grassColor CONSTANT ) + Q_PROPERTY( QColor forestColor READ forestColor CONSTANT ) + Q_PROPERTY( QColor nightColor READ nightColor CONSTANT ) + Q_PROPERTY( QColor whiteColor READ whiteColor CONSTANT ) + Q_PROPERTY( QColor transparentColor READ transparentColor CONSTANT ) + + // Colors - secondary palette + Q_PROPERTY( QColor lightGreenColor READ lightGreenColor CONSTANT ) + Q_PROPERTY( QColor mediumGreenColor READ mediumGreenColor CONSTANT ) + Q_PROPERTY( QColor grayColor READ grayColor CONSTANT ) + + // Colors - additional colors + Q_PROPERTY( QColor sandColor READ sandColor CONSTANT ) + Q_PROPERTY( QColor sunsetColor READ sunsetColor CONSTANT ) + Q_PROPERTY( QColor sunColor READ sunColor CONSTANT ) + Q_PROPERTY( QColor earthColor READ earthColor CONSTANT ) + Q_PROPERTY( QColor roseColor READ roseColor CONSTANT ) + Q_PROPERTY( QColor skyColor READ skyColor CONSTANT ) + Q_PROPERTY( QColor grapeColor READ grapeColor CONSTANT ) + Q_PROPERTY( QColor deepOceanColor READ deepOceanColor CONSTANT ) + Q_PROPERTY( QColor purpleColor READ purpleColor CONSTANT ) + Q_PROPERTY( QColor fieldColor READ fieldColor CONSTANT ) + + // Colors - sentiment colors + Q_PROPERTY( QColor positiveColor READ positiveColor CONSTANT ) + Q_PROPERTY( QColor warningColor READ warningColor CONSTANT ) + Q_PROPERTY( QColor negativeColor READ negativeColor CONSTANT ) + Q_PROPERTY( QColor informativeColor READ informativeColor CONSTANT ) + + // Colors - others + Q_PROPERTY( QColor nightAlphaColor READ nightAlphaColor CONSTANT ) // placeholder input color + Q_PROPERTY( QColor errorBgInputColor READ errorBgInputColor CONSTANT ) // error bg input color + Q_PROPERTY( QColor shadowColor READ shadowColor CONSTANT ) + + // Icons + 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 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 editIcon READ editIcon CONSTANT ) + Q_PROPERTY( QUrl moreIcon READ moreIcon CONSTANT ) + + // Images + Q_PROPERTY( QUrl uploadImage READ uploadImage CONSTANT ) + Q_PROPERTY( QUrl reachedDataLimitImage READ reachedDataLimitImage CONSTANT ) + + // Map items + Q_PROPERTY( double mapItemHeight READ mapItemHeight CONSTANT ) + + // Toolbar + Q_PROPERTY( double toolbarHeight READ toolbarHeight CONSTANT ) + Q_PROPERTY( double menuDrawerHeight READ menuDrawerHeight CONSTANT ) + + public: + explicit MMStyle( qreal dp ) + : mDp( dp ) + {} + ~MMStyle() = default; + + 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 t5() {return fontFactory( 10, true );} + + QFont p1() {return fontFactory( 32, false );} + QFont p2() {return fontFactory( 24, false );} + QFont p3() {return fontFactory( 20, false );} + QFont p4() {return fontFactory( 16, false );} + QFont p5() {return fontFactory( 14, false );} + QFont p6() {return fontFactory( 12, false );} + QFont p7() {return fontFactory( 10, false );} + + QColor grassColor() {return QColor::fromString( "#73D19C" );} + QColor forestColor() {return QColor::fromString( "#004C45" );} + QColor nightColor() {return QColor::fromString( "#12181F" );} + QColor whiteColor() {return QColor::fromString( "#FFFFFF" );} + QColor transparentColor() {return QColor::fromString( "transparent" );} + + QColor lightGreenColor() {return QColor::fromString( "#EFF5F3" );} + QColor mediumGreenColor() {return QColor::fromString( "#B7CDC4" );} + QColor grayColor() {return QColor::fromString( "#E2E2E2" );} + + QColor sandColor() {return QColor::fromString( "#FFF4E2" );} + QColor sunsetColor() {return QColor::fromString( "#FFB673" );} + QColor sunColor() {return QColor::fromString( "#F4CB46" );} + QColor earthColor() {return QColor::fromString( "#4D2A24" );} + QColor roseColor() {return QColor::fromString( "#FFBABC" );} + QColor skyColor() {return QColor::fromString( "#A6CBF4" );} + QColor grapeColor() {return QColor::fromString( "#5A2740" );} + QColor deepOceanColor() {return QColor::fromString( "#1C324A" );} + QColor purpleColor() {return QColor::fromString( "#CCBDF5" );} + QColor fieldColor() {return QColor::fromString( "#9BD1A9" );} + + QColor positiveColor() {return QColor::fromString( "#C2FFA6" );} + QColor warningColor() {return QColor::fromString( "#FFD6A6" );} + QColor negativeColor() {return QColor::fromString( "#FFA6A6" );} + QColor informativeColor() {return QColor::fromString( "#A6F4FF" );} + + QColor nightAlphaColor() {return QColor::fromString( "#AA12181F" );} + 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 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 editIcon() {return QUrl( "qrc:/Edit.svg" );} + QUrl moreIcon() {return QUrl( "qrc:/More.svg" );} + + QUrl uploadImage() {return QUrl( "qrc:/UploadImage.svg" );} + QUrl reachedDataLimitImage() {return QUrl( "qrc:/ReachedDataLimitImage.svg" );} + + double mapItemHeight() {return 50 * mDp;} + + double toolbarHeight() {return 89 * mDp;} + double menuDrawerHeight() {return 67 * mDp;} + + signals: + void styleChanged(); + + private: + QFont fontFactory( int pixelSize, bool bold ) + { + QFont f; + f.setBold( bold ); + f.setPixelSize( pixelSize * mDp ); + return f; + } + + qreal mDp; + +}; + +#endif // MMSTYLE_H diff --git a/app/qml/CMakeLists.txt b/app/qml/CMakeLists.txt index e3ec56391..adfdf6ac8 100644 --- a/app/qml/CMakeLists.txt +++ b/app/qml/CMakeLists.txt @@ -92,6 +92,7 @@ set(MM_QML AccountPage.qml AuthPanel.qml Banner.qml + ChangelogPanel.qml CircularProgressBar.qml CodeScanner.qml CodeScannerOverlay.qml diff --git a/app/qml/ChangelogPanel.qml b/app/qml/ChangelogPanel.qml new file mode 100644 index 000000000..8b9090407 --- /dev/null +++ b/app/qml/ChangelogPanel.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.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import "." // import InputStyle singleton +import "./components" +import lc 1.0 + +Item { + id: root + + signal close + + Keys.onReleased: function( event ) { + if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) { + event.accepted = true + close() + } + } + + MessageDialog { + id: errorDialog + + title: qsTr( "Failed to load changelog" ) + buttons: MessageDialog.Ok + onButtonClicked: { + errorDialog.close() + root.close() + } + } + + Page { + width: parent.width + height: parent.height + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + clip: true + + background: Rectangle { + color: InputStyle.panelBackgroundWhite + } + + header: PanelHeader { + height: InputStyle.rowHeightHeader + width: parent.width + color: InputStyle.clrPanelMain + rowHeight: InputStyle.rowHeightHeader + titleText: qsTr("Changelog") + + onBack: root.close() + withBackButton: true + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: root.width - InputStyle.panelMargin + height: parent.height + + Component.onCompleted: changelogView.model.seeChangelogs() + + Text { + id: subTitle + + anchors.top: title.bottom + text: qsTr("Latest Mergin Maps updates") + wrapMode: Text.WordWrap + width: parent.width + font.pixelSize: InputStyle.fontPixelSizeNormal + color: InputStyle.fontColor + } + + ListView { + id: changelogView + + width: parent.width + anchors.top: subTitle.bottom + anchors.topMargin: InputStyle.panelMargin + anchors.bottom: parent.bottom + spacing: InputStyle.panelMargin + clip: true + model: ChangelogModel { + onErrorMsgChanged: function(msg) { + errorDialog.text = msg + errorDialog.open() + } + } + + delegate: MouseArea { + width: changeItem.width + height: changeItem.height + onClicked: Qt.openUrlExternally(model.link) + + Column { + id: changeItem + width: changelogView.width + + Rectangle { + width: parent.width + height: InputStyle.changelogLineWidth + color: InputStyle.changelogLineWColor + } + Text { + text: Qt.locale().dayName( model.date.getDay(), Locale.ShortFormat ) + ", " + model.date.getDate() + " " + Qt.locale().monthName( model.date.getMonth(), Locale.LongFormat ) + font.italic: true + wrapMode: Text.WordWrap + width: parent.width + font.pixelSize: InputStyle.fontPixelSizeNormal + color: InputStyle.fontColor + } + Text { + text: model.title + font.bold: true; + wrapMode: Text.WordWrap + width: parent.width + font.pixelSize: InputStyle.fontPixelSizeBig + color: InputStyle.fontColor + } + Text { + text: model.description + wrapMode: Text.WordWrap + width: parent.width + font.pixelSize: InputStyle.fontPixelSizeNormal + color: InputStyle.fontColor + } + } + } + + ScrollBar.vertical: ScrollBar { + parent: changelogView.parent + anchors.top: changelogView.top + anchors.left: changelogView.right + anchors.bottom: changelogView.bottom + } + } + } + } +} diff --git a/app/qml/InputStyle.qml b/app/qml/InputStyle.qml index 15eb270e9..4be8e9251 100644 --- a/app/qml/InputStyle.qml +++ b/app/qml/InputStyle.qml @@ -208,6 +208,9 @@ QtObject { property color guidelineColor: mapObjectsColor property real guidelineWidth: 4 * __dp + property real changelogLineWidth: 2 * __dp + property color changelogLineWColor: "lightGray" + property color mapMarkerColor: mapObjectsColor property color mapMarkerBorderColor: "white" diff --git a/app/qml/SettingsPanel.qml b/app/qml/SettingsPanel.qml index dc1c900af..2dd3ea9b2 100644 --- a/app/qml/SettingsPanel.qml +++ b/app/qml/SettingsPanel.qml @@ -351,6 +351,15 @@ Item { } } + // Changelog + PanelItem { + text: qsTr("Changelog") + MouseArea { + anchors.fill: parent + onClicked: stackview.push(changelogPanelComponent) + } + } + // Help PanelItem { text: qsTr("Help") @@ -399,6 +408,14 @@ Item { } } + Component { + id: changelogPanelComponent + ChangelogPanel { + onClose: stackview.pop(null) + Component.onCompleted: forceActiveFocus() + } + } + Component { id: logPanelComponent LogPanel { diff --git a/app/qmlV2/Style.js b/app/qmlV2/Style.js deleted file mode 100644 index 98c8b2567..000000000 --- a/app/qmlV2/Style.js +++ /dev/null @@ -1,83 +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. * - * * - ***************************************************************************/ - -// Fonts - Title -const t1 = { pixelSize: 18 * __dp, bold: true } -const t2 = { pixelSize: 16 * __dp, bold: true } -const t3 = { pixelSize: 14 * __dp, bold: true } -const t4 = { pixelSize: 12 * __dp, bold: true } -const t5 = { pixelSize: 10 * __dp, bold: true } - -// Fonts - Paragraph -const p1 = { pixelSize: 32 * __dp } -const p2 = { pixelSize: 24 * __dp } -const p3 = { pixelSize: 20 * __dp } -const p4 = { pixelSize: 16 * __dp } -const p5 = { pixelSize: 14 * __dp } -const p6 = { pixelSize: 12 * __dp } -const p7 = { pixelSize: 10 * __dp } - -// Colors - pripary palette -const grass = "#73D19C" -const forest = "#004C45" -const night = "#12181F" -const white = "#FFFFFF" -const transparent = "transparent" - -// Colors - secondary palette -const lightGreen = "#EFF5F3" -const mediumGreen = "#B7CDC4" -const gray = "#E2E2E2" - -// Colors - additional colors -const send = "#FFF4E2" -const sunset = "#FFB673" -const sun = "#F4CB46" -const earth = "#4D2A24" -const rose = "#FFBABC" -const sky = "#A6CBF4" -const grape = "#5A2740" -const deepOcean = "#1C324A" -const purple = "#CCBDF5" -const field = "#9BD1A9" - -// Colors - sentiment colors -const positive = "#C2FFA6" -const warning = "#FFD6A6" -const negative = "#FFA6A6" -const informative = "#A6F4FF" - -// Colors - special -const night_6 = "#AA12181F" // placeholder input color -const errorBgInputColor = "#FEFAF9" // error bg input color - -// Icons -const arrowLinkRightIcon= "qrc:/Arrow Link Right.svg" -const searchIcon = "qrc:/Search.svg" -const calendarIcon = "qrc:/Calendar.svg" -const showIcon = "qrc:/Show.svg" -const hideIcon = "qrc:/Hide.svg" -const xMarkIcon = "qrc:/X Mark.svg" -const errorIcon = "qrc:/Error.svg" -const arrowUpIcon = "qrc:/Arrow Up.svg" -const arrowDownIcon = "qrc:/Arrow Down.svg" -const qrCodeIcon = "qrc:/QR Code.svg" -const checkmarkIcon = "qrc:/Checkmark.svg" -const closeButtonIcon = "qrc:/CloseButton.svg" - -// Images -const uploadImage = "qrc:/UploadImage.svg" -const ReachedDataLimitImage = "qrc:/ReachedDataLimitImage.svg" - -// Spacing -const commonSpacing = 20 * __dp - -function dynamicText() { - return "Dynamic text" -} diff --git a/app/qmlV2/component/MMButton.qml b/app/qmlV2/component/MMButton.qml index 6c4585c18..8c1901483 100644 --- a/app/qmlV2/component/MMButton.qml +++ b/app/qmlV2/component/MMButton.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." Button { @@ -20,42 +19,20 @@ Button { contentItem: Text { anchors.centerIn: control - font: Qt.font(Style.t3) + font: __style.t3 text: control.text leftPadding: 32 * __dp rightPadding: 32 * __dp topPadding: 10 * __dp bottomPadding: 10 * __dp - color: control.enabled ? control.down || control.hovered ? Style.grass : Style.forest : Style.forest + color: control.enabled ? control.down || control.hovered ? __style.grassColor : __style.forestColor : __style.forestColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } background: Rectangle { - color: transparent ? "transparent" : control.enabled ? control.down || control.hovered ? Style.forest : Style.grass : Style.mediumGreen + color: transparent ? "transparent" : control.enabled ? control.down || control.hovered ? __style.forestColor : __style.grassColor : __style.mediumGreenColor radius: height / 2 } - -// onPressed: clickTransition.running = true - -// SequentialAnimation { -// id: clickTransition - -// PropertyAnimation { -// target: control -// properties: "scale" -// from: 1 -// to: 0.9 -// duration: 100 -// } - -// PropertyAnimation { -// target: control -// properties: "scale" -// from: 0.9 -// to: 1 -// duration: 100 -// } -// } } diff --git a/app/qmlV2/component/MMButtonInput.qml b/app/qmlV2/component/MMButtonInput.qml new file mode 100644 index 000000000..92c35c598 --- /dev/null +++ b/app/qmlV2/component/MMButtonInput.qml @@ -0,0 +1,146 @@ +/*************************************************************************** + * * + * 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 { + id: control + + property alias title: titleItem.text + property alias text: textField.text + property alias placeholderText: textField.placeholderText + property url iconSource: "" + required property string buttonText + property string warningMsg + property string errorMsg + + signal clicked + + spacing: 6 * __dp + width: 280 * __dp + + Text { + id: titleItem + + width: parent.width + font: __style.p6 + wrapMode: Text.WordWrap + visible: text.length > 0 + } + + Rectangle { + id: rect + + height: 40 * __dp + width: parent.width + color: (errorMsg.length > 0 || warningMsg.length > 0) ? __style.errorBgInputColor : __style.whiteColor + border.color: errorMsg.length > 0 ? __style.negativeColor : warningMsg.length > 0 ? __style.warningColor : __style.forestColor + border.width: enabled ? (textField.activeFocus ? 2*__dp : textField.hovered ? 1*__dp : 0) : 0 + radius: parent.height + + Row { + id: row + + anchors.verticalCenter: parent.verticalCenter + leftPadding: 10 * __dp + + MMIcon { + id: leftIcon + + source: control.iconSource + color: errorMsg.length > 0 ? __style.negativeColor : + warningMsg.length > 0 ? __style.warningColor : + control.enabled ? __style.forestColor : __style.mediumGreenColor + height: rect.height + } + + TextField { + id: textField + + y: 2 * __dp + width: control.width - 2 * row.leftPadding + - (leftIcon.visible ? leftIcon.width : 0) + - (button.visible ? button.width : 0) + height: rect.height - 4 * __dp + color: control.enabled ? __style.nightColor : __style.mediumGreenColor + placeholderTextColor: __style.nightAlphaColor + font: __style.p5 + hoverEnabled: true + anchors.verticalCenter: parent.verticalCenter + background: Rectangle { + color: __style.transparentColor + } + } + + Button { + id: button + + anchors.verticalCenter: parent.verticalCenter + + contentItem: Text { + anchors.centerIn: button + font: __style.t5 + text: control.buttonText + leftPadding: 2 * __dp + rightPadding: 2 * __dp + topPadding: 2 * __dp + bottomPadding: 2 * __dp + color: __style.deepOceanColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: button.enabled ? __style.grassColor : __style.mediumGreenColor + radius: height / 2 + } + + onClicked: control.clicked() + } + } + } + + 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 { + text: errorMsg.length > 0 ? errorMsg : warningMsg + font: __style.t4 + wrapMode: Text.WordWrap + width: messageItem.width - msgRow.spacing - msgIcon.width + visible: errorMsg.length > 0 || warningMsg.length > 0 + } + } + } + + // add whole text to clipboard + function textToClipboard() { + textField.selectAll() + textField.copy() + textField.deselect() + } +} diff --git a/app/qmlV2/component/MMCheckBox.qml b/app/qmlV2/component/MMCheckBox.qml index cb28a4cb5..1c6f109ce 100644 --- a/app/qmlV2/component/MMCheckBox.qml +++ b/app/qmlV2/component/MMCheckBox.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style CheckBox { id: control @@ -23,23 +22,23 @@ CheckBox { x: control.leftPadding y: parent.height / 2 - height / 2 radius: 5 - color: enabled ? ( control.checked ? Style.grass: Style.white ) : Style.white - border.color: enabled ? ( control.checked ? Style.grass: Style.forest ) : Style.mediumGreen + 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 MMIcon { id: icon anchors.centerIn: parent - source: Style.checkmarkIcon - color: control.enabled ? Style.forest : Style.mediumGreen + source: __style.checkmarkIcon + color: control.enabled ? __style.forestColor : __style.mediumGreenColor visible: control.checked } } contentItem: Text { text: control.text - font: Qt.font(Style.p5) + font: __style.p5 color: icon.color verticalAlignment: Text.AlignVCenter leftPadding: control.indicator.width + control.spacing diff --git a/app/qmlV2/component/MMComboBox.qml b/app/qmlV2/component/MMComboBox.qml index 57f0f7f8e..d86b8548a 100644 --- a/app/qmlV2/component/MMComboBox.qml +++ b/app/qmlV2/component/MMComboBox.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style Item { id: control @@ -31,8 +30,8 @@ Item { height: 40 * __dp indicator: MMIcon { - source: popup.visible ? Style.arrowUpIcon : Style.arrowDownIcon - color: control.enabled ? Style.forest : Style.mediumGreen + source: popup.visible ? __style.arrowUpIcon : __style.arrowDownIcon + color: control.enabled ? __style.forestColor : __style.mediumGreenColor x: combobox.width - width - combobox.rightPadding anchors.verticalCenter: parent.verticalCenter } @@ -42,17 +41,17 @@ Item { rightPadding: combobox.indicator.width + combobox.spacing text: combobox.displayText - font: Qt.font(Style.p4) - color: control.enabled ? Style.night : Style.mediumGreen + font: __style.p4 + color: control.enabled ? __style.nightColor : __style.mediumGreenColor verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } background: Rectangle { - color: (errorMsg.length > 0 || warningMsg.length > 0) ? Style.errorBgInputColor : Style.white - border.color: (combobox.activeFocus || combobox.hovered) ? (errorMsg.length > 0 ? Style.negative : - warningMsg.length > 0 ? Style.warning : - Style.forest) : "transparent" + color: (errorMsg.length > 0 || warningMsg.length > 0) ? __style.errorBgInputColor : __style.whiteColor + border.color: (combobox.activeFocus || combobox.hovered) ? (errorMsg.length > 0 ? __style.negativeColor : + warningMsg.length > 0 ? __style.warningColor : + __style.forestColor) : __style.transparentColor border.width: enabled ? (combobox.activeFocus ? 2*__dp : 1*__dp) : 0 radius: parent.height } @@ -75,7 +74,7 @@ Item { } background: Rectangle { - border.color: Style.forest + border.color: __style.forestColor radius: 2 } } @@ -87,13 +86,13 @@ Item { height: 30 * __dp contentItem: Text { text: modelData - color: combobox.highlightedIndex === index ? Style.grass : Style.forest - font: Qt.font(Style.p4) + color: combobox.highlightedIndex === index ? __style.grassColor : __style.forestColor + font: __style.p4 elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } background: Rectangle { - border.color: Style.white + border.color: __style.whiteColor } } @@ -117,13 +116,13 @@ Item { MMIcon { id: msgIcon - source: visible ? Style.errorIcon : "" - color: errorMsg.length > 0 ? Style.negative : Style.warning + source: visible ? __style.errorIcon : "" + color: errorMsg.length > 0 ? __style.negativeColor : __style.warningColor visible: errorMsg.length > 0 || warningMsg.length > 0 } Text { text: errorMsg.length > 0 ? errorMsg : warningMsg - font: Qt.font(Style.t4) + font: __style.t4 wrapMode: Text.WordWrap width: messageItem.width - msgRow.spacing - msgIcon.width visible: errorMsg.length > 0 || warningMsg.length > 0 diff --git a/app/qmlV2/component/MMComponent_reachedDataLimit.qml b/app/qmlV2/component/MMComponent_reachedDataLimit.qml index fc21b882d..3256e0999 100644 --- a/app/qmlV2/component/MMComponent_reachedDataLimit.qml +++ b/app/qmlV2/component/MMComponent_reachedDataLimit.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." Item { @@ -28,9 +27,9 @@ Item { id: component Column { - width: control.width - 2 * Style.commonSpacing - spacing: Style.commonSpacing / 2 - topPadding: Style.commonSpacing + width: control.width - 40 * __dp + spacing: 10 * __dp + topPadding: 20 * __dp Item { width: parent.width @@ -39,8 +38,8 @@ Item { Text { width: parent.width text: qsTr("Data to sync") - font: Qt.font(Style.p5) - color: Style.night + font: __style.p5 + color: __style.nightColor horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } @@ -49,14 +48,14 @@ Item { width: parent.width text: control.dataToSync - font: Qt.font(Style.t3) - color: Style.night + font: __style.t3 + color: __style.nightColor horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter } } - Rectangle { width: parent.width; height: 1; color: Style.gray } + Rectangle { width: parent.width; height: 1; color: __style.grayColor } Item { width: parent.width @@ -65,8 +64,8 @@ Item { Text { width: parent.width text: qsTr("Using") - font: Qt.font(Style.p5) - color: Style.night + font: __style.p5 + color: __style.nightColor horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } @@ -75,8 +74,8 @@ Item { width: parent.width text: control.dataUsing - font: Qt.font(Style.t3) - color: Style.night + font: __style.t3 + color: __style.nightColor horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter } @@ -86,7 +85,7 @@ Item { position: control.usedData } - Rectangle { width: parent.width; height: 1; color: Style.gray } + Rectangle { width: parent.width; height: 1; color: __style.grayColor } Item { width: parent.width @@ -95,8 +94,8 @@ Item { Text { width: parent.width text: qsTr("Plan") - font: Qt.font(Style.p5) - color: Style.night + font: __style.p5 + color: __style.nightColor horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } @@ -105,8 +104,8 @@ Item { width: parent.width text: control.plan - font: Qt.font(Style.t3) - color: Style.night + font: __style.t3 + color: __style.nightColor horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter } diff --git a/app/qmlV2/component/MMDrawer.qml b/app/qmlV2/component/MMDrawer.qml index 6389957d2..7a2ad8f3e 100644 --- a/app/qmlV2/component/MMDrawer.qml +++ b/app/qmlV2/component/MMDrawer.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." Drawer { @@ -38,30 +37,30 @@ Drawer { anchors.right: parent.right height: 2 * radius anchors.topMargin: -radius - radius: Style.commonSpacing + radius: 20 * __dp } Rectangle { id: roundedRect anchors.fill: parent - color: Style.white + color: __style.whiteColor Column { id: mainColumn width: parent.width - spacing: Style.commonSpacing - leftPadding: Style.commonSpacing - rightPadding: Style.commonSpacing - bottomPadding: Style.commonSpacing + spacing: 20 * __dp + leftPadding: 20 * __dp + rightPadding: 20 * __dp + bottomPadding: 20 * __dp Image { id: closeButton - source: Style.closeButtonIcon + source: __style.closeButtonIcon anchors.right: parent.right - anchors.rightMargin: Style.commonSpacing + anchors.rightMargin: 20 * __dp MouseArea { anchors.fill: parent @@ -79,9 +78,9 @@ Drawer { id: title anchors.horizontalCenter: parent.horizontalCenter - font: Qt.font(Style.t1) - width: parent.width - 2*Style.commonSpacing - color: Style.forest + font: __style.t1 + width: parent.width - 2*20 * __dp + color: __style.forestColor visible: text.length > 0 horizontalAlignment: Text.AlignHCenter } @@ -90,9 +89,9 @@ Drawer { id: description anchors.horizontalCenter: parent.horizontalCenter - font: Qt.font(Style.p5) - width: parent.width - 2*Style.commonSpacing - color: Style.night + font: __style.p5 + width: parent.width - 2*20 * __dp + color: __style.nightColor visible: text.length > 0 horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap @@ -102,24 +101,23 @@ Drawer { Rectangle { anchors.horizontalCenter: parent.horizontalCenter visible: boundedDescription.text.length > 0 - width: parent.width - 2*Style.commonSpacing + width: parent.width - 2*20 * __dp height: boundedDescription.height radius: 16 * __dp - - color: Style.lightGreen + color: __style.lightGreenColor Text { id: boundedDescription anchors.horizontalCenter: parent.horizontalCenter - font: Qt.font(Style.p6) + font: __style.p6 width: parent.width - color: Style.night + color: __style.nightColor visible: text.length > 0 horizontalAlignment: Text.AlignLeft wrapMode: Text.WordWrap lineHeight: 1.6 - padding: Style.commonSpacing + padding: 20 * __dp } } @@ -134,7 +132,7 @@ Drawer { MMButton { id: primaryButton - width: parent.width - 2*Style.commonSpacing + width: parent.width - 2*20 * __dp visible: text.length > 0 onClicked: primaryButtonClicked() @@ -143,7 +141,7 @@ Drawer { MMButton { id: secondaryButton - width: parent.width - 2*Style.commonSpacing + width: parent.width - 2*20 * __dp visible: text.length > 0 transparent: true topPadding: 0 diff --git a/app/qmlV2/component/MMIcon.qml b/app/qmlV2/component/MMIcon.qml index 2be5fb0d6..eb8c24de1 100644 --- a/app/qmlV2/component/MMIcon.qml +++ b/app/qmlV2/component/MMIcon.qml @@ -9,7 +9,6 @@ import QtQuick import Qt5Compat.GraphicalEffects -import "../Style.js" as Style Item { id: control @@ -22,7 +21,8 @@ Item { Image { id: icon - source: Style.arrowLinkRightIcon + + source: __style.arrowLinkRightIcon anchors.centerIn: control } diff --git a/app/qmlV2/component/MMInput.qml b/app/qmlV2/component/MMInput.qml index 85a810435..169faff1c 100644 --- a/app/qmlV2/component/MMInput.qml +++ b/app/qmlV2/component/MMInput.qml @@ -10,51 +10,27 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style -Item { +Column { id: control - enum Type { Normal, Password, Search, Calendar, Scan, CopyButton } - - property int type: MMInput.Type.Normal + property alias title: titleItem.text property alias text: textField.text property alias placeholderText: textField.placeholderText + property url iconSource: "" property string warningMsg property string errorMsg + spacing: 6 * __dp width: 280 * __dp - height: rect.height + messageItem.height - Item { - id: messageItem + Text { + id: titleItem width: parent.width - anchors.left: parent.left - anchors.top: rect.bottom - anchors.topMargin: 6 * __dp - height: msgRow.height - - Row { - id: msgRow - - spacing: 4 * __dp - - MMIcon { - id: msgIcon - - source: visible ? Style.errorIcon : "" - color: errorMsg.length > 0 ? Style.negative : Style.warning - visible: errorMsg.length > 0 || warningMsg.length > 0 - } - Text { - text: errorMsg.length > 0 ? errorMsg : warningMsg - font: Qt.font(Style.t4) - wrapMode: Text.WordWrap - width: messageItem.width - msgRow.spacing - msgIcon.width - visible: errorMsg.length > 0 || warningMsg.length > 0 - } - } + font: __style.p6 + wrapMode: Text.WordWrap + visible: text.length > 0 } Rectangle { @@ -62,11 +38,9 @@ Item { height: 40 * __dp width: parent.width - color: (errorMsg.length > 0 || warningMsg.length > 0) ? Style.errorBgInputColor : Style.white - border.color: (textField.activeFocus || textField.hovered) ? (errorMsg.length > 0 ? Style.negative : - warningMsg.length > 0 ? Style.warning : - Style.forest) : "transparent" - border.width: enabled ? (textField.activeFocus ? 2*__dp : 1*__dp) : 0 + color: (errorMsg.length > 0 || warningMsg.length > 0) ? __style.errorBgInputColor : __style.whiteColor + border.color: errorMsg.length > 0 ? __style.negativeColor : warningMsg.length > 0 ? __style.warningColor : __style.forestColor + border.width: enabled ? (textField.activeFocus ? 2*__dp : textField.hovered ? 1*__dp : 0) : 0 radius: parent.height Row { @@ -78,14 +52,11 @@ Item { MMIcon { id: leftIcon - source: control.type === MMInput.Type.Search ? Style.searchIcon : - control.type === MMInput.Type.Calendar ? Style.calendarIcon : "" - color: errorMsg.length > 0 ? Style.negative : - warningMsg.length > 0 ? Style.warning : - control.enabled ? Style.forest : Style.mediumGreen - width: height + source: control.iconSource + color: errorMsg.length > 0 ? __style.negativeColor : + warningMsg.length > 0 ? __style.warningColor : + control.enabled ? __style.forestColor : __style.mediumGreenColor height: rect.height - visible: control.type === MMInput.Type.Search || control.type === MMInput.Type.Calendar } TextField { @@ -94,81 +65,61 @@ Item { y: 2 * __dp width: control.width - 2 * row.leftPadding - (leftIcon.visible ? leftIcon.width : 0) - - (rightIcon.visible ? rightIcon.width : 0) - - (button.visible ? button.width : 0) + - (clearButton.visible ? clearButton.width : 0) height: rect.height - 4 * __dp - color: control.enabled ? Style.night : Style.mediumGreen - placeholderTextColor: Style.night_6 - font: Qt.font(Style.p5) + color: control.enabled ? __style.nightColor : __style.mediumGreenColor + placeholderTextColor: __style.nightAlphaColor + font: __style.p5 hoverEnabled: true anchors.verticalCenter: parent.verticalCenter - echoMode: (control.type === MMInput.Type.Password && !rightIcon.pressed) ? TextInput.Password : TextInput.Normal background: Rectangle { - color: Style.transparent + color: __style.transparentColor } } MMIcon { - id: rightIcon + id: clearButton property bool pressed: false - source: control.type === MMInput.Type.Password ? (pressed ? Style.hideIcon : Style.showIcon) : - control.type === MMInput.Type.Scan ? Style.qrCodeIcon : - (textField.activeFocus && textField.text.length>0) ? Style.xMarkIcon : "" - color: control.enabled ? Style.forest : Style.mediumGreen + source: __style.xMarkIcon + color: control.enabled ? __style.forestColor : __style.mediumGreenColor width: visible ? height : 0 height: rect.height - visible: control.type === MMInput.Type.Password || - control.type === MMInput.Type.Scan || - ((control.type !== MMInput.Type.CopyButton) && textField.activeFocus && textField.text.length>0) + visible: textField.activeFocus && textField.text.length>0 MouseArea { anchors.fill: parent - onClicked: click() - - function click() { - if(control.type === MMInput.Type.Password) { - rightIcon.pressed = !rightIcon.pressed - } - else if(textField.activeFocus && textField.text.length>0) { - textField.text = "" - } - } + onClicked: textField.text = "" } } + } + } - Button { - id: button + Item { + id: messageItem - visible: control.type === MMInput.Type.CopyButton - anchors.verticalCenter: parent.verticalCenter + width: parent.width + height: msgRow.height - contentItem: Text { - anchors.centerIn: button - font: Qt.font(Style.t5) - text: qsTr("Copy") - leftPadding: 2 * __dp - rightPadding: 2 * __dp - topPadding: 2 * __dp - bottomPadding: 2 * __dp - color: Style.deepOcean - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } + Row { + id: msgRow - background: Rectangle { - color: button.enabled ? Style.grass : Style.mediumGreen - radius: height / 2 - } + spacing: 4 * __dp - onClicked: { - textField.selectAll() - textField.copy() - textField.deselect() - } - } + MMIcon { + id: msgIcon + source: visible ? __style.errorIcon : "" + color: errorMsg.length > 0 ? __style.negativeColor : __style.warningColor + visible: errorMsg.length > 0 || warningMsg.length > 0 + } + Text { + text: errorMsg.length > 0 ? errorMsg : warningMsg + font: __style.t4 + wrapMode: Text.WordWrap + width: messageItem.width - msgRow.spacing - msgIcon.width + visible: errorMsg.length > 0 || warningMsg.length > 0 + } } } } diff --git a/app/qmlV2/component/MMLink.qml b/app/qmlV2/component/MMLink.qml index feae882b0..f7ff33e8d 100644 --- a/app/qmlV2/component/MMLink.qml +++ b/app/qmlV2/component/MMLink.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." Button { @@ -23,9 +22,9 @@ Button { Text { id: text - font: Qt.font(Style.t3) + font: __style.t3 text: control.text - color: control.enabled ? control.down || control.hovered ? Style.night : Style.forest : Style.mediumGreen + color: control.enabled ? control.down || control.hovered ? __style.nightColor : __style.forestColor : __style.mediumGreenColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -33,12 +32,12 @@ Button { } MMIcon { - source: Style.arrowLinkRightIcon + source: __style.arrowLinkRightIcon color: text.color } } background: Rectangle { - color: Style.transparent + color: __style.transparentColor } } diff --git a/app/qmlV2/component/MMLinkButton.qml b/app/qmlV2/component/MMLinkButton.qml index 66ac81277..b87fc3e8c 100644 --- a/app/qmlV2/component/MMLinkButton.qml +++ b/app/qmlV2/component/MMLinkButton.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." Button { @@ -18,21 +17,21 @@ Button { contentItem: Text { anchors.centerIn: control - font: Qt.font(Style.t3) + font: __style.t3 text: control.text leftPadding: 32 * __dp rightPadding: 32 * __dp topPadding: 10 * __dp bottomPadding: 10 * __dp - color: control.enabled ? Style.forest : Style.mediumGreen + color: control.enabled ? __style.forestColor : __style.mediumGreenColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } background: Rectangle { - color: control.enabled ? control.down || control.hovered ? Style.grass : Style.white : Style.white - border.color: control.enabled ? control.down || control.hovered ? Style.transparent : Style.forest : Style.mediumGreen + color: control.enabled ? control.down || control.hovered ? __style.grassColor : __style.whiteColor : __style.whiteColor + border.color: control.enabled ? control.down || control.hovered ? __style.transparentColor : __style.forestColor : __style.mediumGreenColor border.width: 2 * __dp radius: height / 2 } diff --git a/app/qmlV2/component/MMMapButton.qml b/app/qmlV2/component/MMMapButton.qml new file mode 100644 index 000000000..1dfab7eb7 --- /dev/null +++ b/app/qmlV2/component/MMMapButton.qml @@ -0,0 +1,43 @@ +/*************************************************************************** + * * + * 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 + +Item { + id: control + + width: height + height: __style.mapItemHeight + + property alias iconSource: icon.source + + signal clicked + + Rectangle { + width: parent.width + height: parent.height + radius: control.height / 2 + color: __style.whiteColor + + layer.enabled: true + layer.effect: MMShadow {} + + MMIcon { + id: icon + + anchors.centerIn: parent + color: __style.forestColor + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked() + } + } +} diff --git a/app/qmlV2/component/MMMapLabel.qml b/app/qmlV2/component/MMMapLabel.qml new file mode 100644 index 000000000..b5921c2d6 --- /dev/null +++ b/app/qmlV2/component/MMMapLabel.qml @@ -0,0 +1,68 @@ +/*************************************************************************** + * * + * 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 "." + +Item { + id: control + + width: text.width + height: __style.mapItemHeight + + signal clicked + + required property string text + property url iconSource: "" + property color bgColor: __style.positiveColor + property color textColor: __style.forestColor + + Rectangle { + width: row.width + height: parent.height + radius: control.height / 2 + color: control.bgColor + + layer.enabled: true + layer.effect: MMShadow {} + + Row { + id: row + + anchors.centerIn: parent + leftPadding: 20 * __dp + rightPadding: leftPadding + spacing: 4 * __dp + height: parent.height + + MMIcon { + id: icon + + source: control.iconSource ? control.iconSource : "" + color: text.color + height: parent.height + } + + Text { + id: text + + color: control.textColor + text: control.text + font: __style.t3 + verticalAlignment: Text.AlignVCenter + height: parent.height + } + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked() + } + } +} diff --git a/app/qmlV2/component/MMMenuDrawer.qml b/app/qmlV2/component/MMMenuDrawer.qml new file mode 100644 index 000000000..7289762e7 --- /dev/null +++ b/app/qmlV2/component/MMMenuDrawer.qml @@ -0,0 +1,87 @@ +/*************************************************************************** + * * + * 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 "." + +Drawer { + id: control + + property alias title: title.text + property alias model: menuView.model + + signal clicked(var button) + + 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: 20 * __dp + rightPadding: 20 * __dp + bottomPadding: 20 * __dp + + Image { + id: closeButton + + source: __style.closeButtonIcon + anchors.right: parent.right + anchors.rightMargin: 20 * __dp + + MouseArea { + anchors.fill: parent + onClicked: control.visible = false + } + } + + Text { + id: title + + anchors.horizontalCenter: parent.horizontalCenter + font: __style.t1 + width: parent.width - 40 * __dp + color: __style.forestColor + visible: text.length > 0 + horizontalAlignment: Text.AlignHCenter + } + + GridView { + id: menuView + + width: parent.width - 40 * __dp + height: model ? model.count * __style.menuDrawerHeight : 0 + cellWidth: width + cellHeight: __style.menuDrawerHeight + interactive: false + } + } + } +} diff --git a/app/qmlV2/component/MMNotification.qml b/app/qmlV2/component/MMNotification.qml new file mode 100644 index 000000000..782f3cc29 --- /dev/null +++ b/app/qmlV2/component/MMNotification.qml @@ -0,0 +1,96 @@ +/*************************************************************************** + * * + * 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 notificationType 1.0 + +Rectangle { + id: notification + + width: listView.width - 40 * __dp + height: text.height + 2 * 15 * __dp + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined + + readonly property int innerSpacing: 5 * __dp + + radius: 12 * __dp + color: { + switch( type ) { + case NotificationType.Information: return __style.informativeColor + case NotificationType.Success: return __style.positiveColor + case NotificationType.Warning: return __style.warningColor + case NotificationType.Error: return __style.negativeColor + default: return __style.positiveColor + } + } + + MMIcon { + id: leftIcon + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 20 * __dp + width: 18 * __dp + height: 18 * __dp + color: text.color + visible: icon !== NotificationType.None + source: { + switch( icon ) { + case NotificationType.None: return __style.checkmarkIcon + case NotificationType.Waiting: return __style.waitingIcon + case NotificationType.Check: return __style.checkmarkIcon + } + } + } + + Text { + id: text + + anchors.verticalCenter: parent.verticalCenter + anchors.left: leftIcon.right + width: parent.width - 60 * __dp - closeButton.width - leftIcon.width + text: message + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + leftPadding: 20 * __dp - notification.innerSpacing + font: __style.t3 + clip: true + maximumLineCount: 3 + wrapMode: Text.WordWrap + lineHeight: 1.4 + elide: Text.ElideRight + color: { + switch( type ) { + case NotificationType.Information: return __style.deepOceanColor + case NotificationType.Success: return __style.forestColor + case NotificationType.Warning: return __style.earthColor + case NotificationType.Error: return __style.grapeColor + } + } + } + + MMIcon { + id: closeButton + + anchors.right: parent.right + anchors.rightMargin: 20 * __dp + anchors.verticalCenter: parent.verticalCenter + scale: maRemove.containsMouse ? 1.2 : 1 + source: __style.closeIcon + color: text.color + + MouseArea { + id: maRemove + + anchors.fill: parent + hoverEnabled: true + onClicked: notificationModel.remove(id) + } + } +} diff --git a/app/qmlV2/component/MMNotificationView.qml b/app/qmlV2/component/MMNotificationView.qml new file mode 100644 index 000000000..b3c81de4c --- /dev/null +++ b/app/qmlV2/component/MMNotificationView.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 + +Item { + id: control + + anchors.top: parent.top + anchors.topMargin: 20 * __dp + width: parent.width + height: parent.height + + // just for information - will be removed in release version + Rectangle { + anchors.bottom: parent.bottom + width: control.width + height: 20 + color: __style.whiteColor + + Text { + text: listView.count + anchors.centerIn: parent + color: __style.forestColor + } + } + + ListView { + id: listView + + anchors.top: parent.top + width: parent.width + height: contentHeight + spacing: 3 * __dp + clip: true + model: notificationModel + delegate: MMNotification { + + } + + add: Transition { + NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 200 } + NumberAnimation { property: "scale"; easing.type: Easing.OutCubic; from: 0; to: 1.0; duration: 200 } + } + + } +} diff --git a/app/qmlV2/component/MMPasswordInput.qml b/app/qmlV2/component/MMPasswordInput.qml new file mode 100644 index 000000000..5e045c115 --- /dev/null +++ b/app/qmlV2/component/MMPasswordInput.qml @@ -0,0 +1,133 @@ +/*************************************************************************** + * * + * 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 { + id: control + + property alias title: titleItem.text + property alias text: textField.text + property alias placeholderText: textField.placeholderText + required property string regexp + property url iconSource: "" + property string warningMsg + property string errorMsg + + spacing: 6 * __dp + width: 280 * __dp + + Text { + id: titleItem + + width: parent.width + font: __style.p6 + wrapMode: Text.WordWrap + visible: text.length > 0 + } + + Rectangle { + id: rect + + height: 40 * __dp + width: parent.width + color: (errorMsg.length > 0 || warningMsg.length > 0) ? __style.errorBgInputColor : __style.whiteColor + border.color: isPasswordCorrect(textField.text) ? __style.forestColor : errorMsg.length > 0 ? __style.negativeColor : warningMsg.length > 0 ? __style.warningColor : __style.forestColor + border.width: enabled ? (textField.activeFocus ? 2*__dp : textField.hovered ? 1*__dp : 0) : 0 + radius: parent.height + + Row { + id: row + + anchors.verticalCenter: parent.verticalCenter + leftPadding: 10 * __dp + + MMIcon { + id: leftIcon + + source: control.iconSource + color: errorMsg.length > 0 ? __style.negativeColor : + warningMsg.length > 0 ? __style.warningColor : + control.enabled ? __style.forestColor : __style.mediumGreenColor + height: rect.height + } + + TextField { + id: textField + + y: 2 * __dp + width: control.width - 2 * row.leftPadding + - (leftIcon.visible ? leftIcon.width : 0) + - (eyeButton.visible ? eyeButton.width : 0) + height: rect.height - 4 * __dp + color: control.enabled ? __style.nightColor : __style.mediumGreenColor + placeholderTextColor: __style.nightAlphaColor + font: __style.p5 + hoverEnabled: true + anchors.verticalCenter: parent.verticalCenter + echoMode: eyeButton.pressed ? TextInput.Normal : TextInput.Password + background: Rectangle { + color: __style.transparentColor + } + } + + MMIcon { + id: eyeButton + + property bool pressed: false + source: pressed ? __style.hideIcon : __style.showIcon + color: control.enabled ? __style.forestColor : __style.mediumGreenColor + width: visible ? height : 0 + height: rect.height + + MouseArea { + anchors.fill: parent + onClicked: eyeButton.pressed = !eyeButton.pressed + } + } + } + } + + 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: msg.visible + } + Text { + id: msg + + text: errorMsg.length > 0 ? errorMsg : warningMsg + font: __style.t4 + wrapMode: Text.WordWrap + width: messageItem.width - msgRow.spacing - msgIcon.width + visible: (errorMsg.length > 0 || warningMsg.length > 0) && !isPasswordCorrect(textField.text) + } + } + } + + function isPasswordCorrect(pwd) { + let pwdRegexp = new RegExp(control.regexp) + return pwdRegexp.test(pwd) + } +} diff --git a/app/qmlV2/component/MMProgressBar.qml b/app/qmlV2/component/MMProgressBar.qml index 47641ee78..d3df3f6e3 100644 --- a/app/qmlV2/component/MMProgressBar.qml +++ b/app/qmlV2/component/MMProgressBar.qml @@ -8,7 +8,6 @@ ***************************************************************************/ import QtQuick -import "../Style.js" as Style import "." Rectangle { @@ -18,13 +17,13 @@ Rectangle { width: parent.width height: 12 * __dp - color: Style.lightGreen + color: __style.lightGreenColor radius: height / 2 Rectangle { width: parent.width * control.position height: parent.height - color: Style.grass + color: __style.grassColor radius: height / 2 } } diff --git a/app/qmlV2/component/MMRadioButton.qml b/app/qmlV2/component/MMRadioButton.qml index b4a2bb62a..b9115939a 100644 --- a/app/qmlV2/component/MMRadioButton.qml +++ b/app/qmlV2/component/MMRadioButton.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style RadioButton { id: control @@ -23,23 +22,23 @@ RadioButton { x: control.leftPadding y: parent.height / 2 - height / 2 radius: 12 - color: enabled ? ( control.checked ? Style.grass: Style.white ) : Style.white - border.color: enabled ? ( control.checked ? Style.grass: Style.forest ) : Style.mediumGreen + 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 MMIcon { id: icon anchors.centerIn: parent - source: Style.checkmarkIcon - color: control.enabled ? Style.forest : Style.mediumGreen + source: __style.checkmarkIcon + color: control.enabled ? __style.forestColor : __style.mediumGreenColor visible: control.checked } } contentItem: Text { text: control.text - font: Qt.font(Style.p5) + font: __style.p5 color: icon.color verticalAlignment: Text.AlignVCenter leftPadding: control.indicator.width + control.spacing diff --git a/app/qmlV2/component/MMRoundButton.qml b/app/qmlV2/component/MMRoundButton.qml index cc12ca7d2..1568a32b2 100644 --- a/app/qmlV2/component/MMRoundButton.qml +++ b/app/qmlV2/component/MMRoundButton.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." RoundButton { @@ -22,12 +21,12 @@ RoundButton { contentItem: MMIcon { id: icon - source: Style.arrowLinkRightIcon - color: control.enabled ? control.down || control.hovered ? Style.grass : Style.forest : Style.forest + source: __style.arrowLinkRightIcon + color: control.enabled ? control.down || control.hovered ? __style.grassColor : __style.forestColor : __style.forestColor } background: Rectangle { - color: control.enabled ? control.down || control.hovered ? Style.forest : Style.grass : Style.mediumGreen + color: control.enabled ? control.down || control.hovered ? __style.forestColor : __style.grassColor : __style.mediumGreenColor radius: control.implicitHeight / 2 } } diff --git a/app/qmlV2/component/MMRoundLinkButton.qml b/app/qmlV2/component/MMRoundLinkButton.qml index 37b4c4634..e6bd31db5 100644 --- a/app/qmlV2/component/MMRoundLinkButton.qml +++ b/app/qmlV2/component/MMRoundLinkButton.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style import "." RoundButton { @@ -22,13 +21,14 @@ RoundButton { contentItem: MMIcon { id: icon - source: Style.arrowLinkRightIcon - color: control.enabled ? Style.forest : Style.mediumGreen + source: __style.arrowLinkRightIcon + color: control.enabled ? __style.forestColor : __style.mediumGreenColor } background: Rectangle { - color: control.enabled ? control.down || control.hovered ? Style.grass : Style.white : Style.white - border.color: control.enabled ? control.down || control.hovered ? Style.transparent : Style.forest : Style.mediumGreen + color: control.enabled ? control.down || control.hovered ? __style.grassColor : __style.whiteColor : __style.whiteColor + border.color: control.enabled ? control.down || control.hovered ? __style.transparentColor : __style.forestColor : __style.mediumGreenColor + border.width: 2 * __dp radius: control.implicitHeight / 2 } diff --git a/app/qmlV2/component/MMShadow.qml b/app/qmlV2/component/MMShadow.qml new file mode 100644 index 000000000..ce99955aa --- /dev/null +++ b/app/qmlV2/component/MMShadow.qml @@ -0,0 +1,20 @@ +/*************************************************************************** + * * + * 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 Qt5Compat.GraphicalEffects + +DropShadow { + id: shadow + horizontalOffset: 2 * __dp + verticalOffset: 3 * __dp + radius: 7 * __dp + color: __style.shadowColor + transparentBorder: true +} diff --git a/app/qmlV2/component/MMSwitch.qml b/app/qmlV2/component/MMSwitch.qml index 27cfcc08c..0da197df9 100644 --- a/app/qmlV2/component/MMSwitch.qml +++ b/app/qmlV2/component/MMSwitch.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style Switch { id: control @@ -20,8 +19,8 @@ Switch { contentItem: Text { text: (control.textOn.length > 0 && control.textOff.length > 0) ? (control.checked ? control.textOn : control.textOff) : control.text - font: Qt.font(Style.p5) - color: control.enabled ? Style.forest : Style.mediumGreen + font: __style.p5 + color: control.enabled ? __style.forestColor : __style.mediumGreenColor verticalAlignment: Text.AlignVCenter leftPadding: control.indicator.width + control.spacing } @@ -32,16 +31,14 @@ Switch { x: control.leftPadding y: parent.height / 2 - height / 2 radius: implicitHeight / 2 - color: control.checked ? Style.grass : Style.white + color: control.checked ? __style.grassColor : __style.whiteColor Rectangle { x: control.checked ? parent.width - width - radius/2 : radius/2 width: 20 height: width radius: width / 2 - color: control.down ? Style.mediumGreen : Style.white - border.color: control.enabled ? Style.forest : Style.mediumGreen - border.width: 6 + color: control.enabled ? __style.forestColor : __style.mediumGreenColor anchors.verticalCenter: parent.verticalCenter } } diff --git a/app/qmlV2/component/MMTextArea.qml b/app/qmlV2/component/MMTextArea.qml index 84920ebb5..50c5b6ad9 100644 --- a/app/qmlV2/component/MMTextArea.qml +++ b/app/qmlV2/component/MMTextArea.qml @@ -10,7 +10,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import "../Style.js" as Style Item { id: control @@ -38,15 +37,15 @@ Item { height: control.autoHeight ? ( contentHeight+10 > control.minHeight ? contentHeight+10 > control.maxHeight ? control.maxHeight : contentHeight+10 : control.minHeight ) : control.areaHeight width: parent.width hoverEnabled: true - placeholderTextColor: Style.night_6 - color: control.enabled ? Style.night : Style.mediumGreen - font: Qt.font(Style.p5) + placeholderTextColor: __style.nightAlphaColor + color: control.enabled ? __style.nightColor : __style.mediumGreenColor + font: __style.p5 background: Rectangle { - color: (errorMsg.length > 0 || warningMsg.length > 0) ? Style.errorBgInputColor : Style.white - border.color: (textArea.activeFocus || textArea.hovered) ? (errorMsg.length > 0 ? Style.negative : - warningMsg.length > 0 ? Style.warning : - Style.forest) : "transparent" + color: (errorMsg.length > 0 || warningMsg.length > 0) ? __style.errorBgInputColor : __style.whiteColor + border.color: (textArea.activeFocus || textArea.hovered) ? (errorMsg.length > 0 ? __style.negativeColor : + warningMsg.length > 0 ? __style.warningColor : + __style.forestColor) : __style.transparentColor border.width: enabled ? (textArea.activeFocus ? 2*__dp : 1*__dp) : 0 radius: 10 * __dp } @@ -69,13 +68,13 @@ Item { MMIcon { id: msgIcon - source: visible ? Style.errorIcon : "" - color: errorMsg.length > 0 ? Style.negative : Style.warning + source: visible ? __style.errorIcon : "" + color: errorMsg.length > 0 ? __style.negativeColor : __style.warningColor visible: errorMsg.length > 0 || warningMsg.length > 0 } Text { text: errorMsg.length > 0 ? errorMsg : warningMsg - font: Qt.font(Style.t4) + font: __style.t4 wrapMode: Text.WordWrap width: messageItem.width - msgRow.spacing - msgIcon.width visible: errorMsg.length > 0 || warningMsg.length > 0 diff --git a/app/qmlV2/component/MMToolbar.qml b/app/qmlV2/component/MMToolbar.qml new file mode 100644 index 000000000..267a42b72 --- /dev/null +++ b/app/qmlV2/component/MMToolbar.qml @@ -0,0 +1,126 @@ +/*************************************************************************** + * * + * 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 Qt5Compat.GraphicalEffects + +Rectangle { + id: control + + signal clicked + + required property var model + + readonly property double minimumToolbarButtonWidth: 100 * __dp + + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: __style.toolbarHeight + color: __style.forestColor + + onWidthChanged: setupBottomBar() + + // buttons shown inside toolbar + ObjectModel { + id: visibleButtonModel + } + + // buttons that are not shown inside toolbar, due to small space + ObjectModel { + id: invisibleButtonModel + } + + GridView { + id: buttonView + + model: visibleButtonModel + anchors.fill: parent + leftMargin: 20 * __dp + rightMargin: 20 * __dp + cellHeight: __style.toolbarHeight + interactive: false + } + + MMMenuDrawer { + id: menu + + title: qsTr("More options") + model: invisibleButtonModel + onClicked: function(button) { + menu.visible = false + buttonClicked(button) + } + } + + // Button More '...' + Component { + id: componentMore + MMToolbarButton { + text: qsTr("More") + iconSource: __style.moreIcon + onClicked: menu.visible = true + } + } + Loader { id: buttonMore; sourceComponent: componentMore; visible: false } + + function setupBottomBar() { + var m = control.model + var c = m.count + var w = control.width - 40 * __dp + var button + + // add all buttons (max 4) into toolbar + visibleButtonModel.clear() + if(c <= 4 || w >= c*control.minimumToolbarButtonWidth) { + for( var i = 0; i < c; i++ ) { + button = m.get(i) + if(button.isMenuButton !== undefined) + button.isMenuButton = false + button.width = Math.floor(w / c) + visibleButtonModel.append(button) + } + buttonView.cellWidth = Math.floor(w / c) + } + else { + // not all buttons are visible in toolbar due to width + // the past of them will apper in the menu inside '...' button + var maxVisible = Math.floor(w/control.minimumToolbarButtonWidth) + if(maxVisible<4) + maxVisible = 4 + for( i = 0; i < maxVisible-1; i++ ) { + if(maxVisible===4 || w >= i*control.minimumToolbarButtonWidth) { + button = m.get(i) + button.isMenuButton = false + button.width = Math.floor(w / maxVisible) + visibleButtonModel.append(button) + } + } + // add More '...' button + button = buttonMore + button.visible = true + button.width = maxVisible ? w / maxVisible : w + visibleButtonModel.append( button ) + buttonView.cellWidth = Math.floor(maxVisible ? w / maxVisible : w) + + // add all other buttons inside the '...' button + invisibleButtonModel.clear() + for( i = maxVisible-1; i < c; i++ ) { + if(i<0) + continue + button = m.get(i) + button.isMenuButton = true + button.width = Math.floor(w) + invisibleButtonModel.append(button) + } + } + } +} diff --git a/app/qmlV2/component/MMToolbarButton.qml b/app/qmlV2/component/MMToolbarButton.qml new file mode 100644 index 000000000..16b2d576d --- /dev/null +++ b/app/qmlV2/component/MMToolbarButton.qml @@ -0,0 +1,83 @@ +/*************************************************************************** + * * + * 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 + + required property var iconSource + required property string text + property var type: MMToolbarButton.Button.Normal + property bool isMenuButton: false + + enum Button { Normal, Save } + + height: isMenuButton ? __style.menuDrawerHeight/2 : __style.toolbarHeight + + Rectangle { + width: parent.width - 10 * __dp + height: parent.height - 10 * __dp + anchors.centerIn: parent + clip: control.type !== MMToolbarButton.Button.Save + color: __style.transparentColor + visible: !control.isMenuButton + + Image { + id: icon + + source: control.iconSource + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 40 * __dp + (control.type === MMToolbarButton.Button.Save ? 14 * __dp : 0) + + Rectangle { + visible: control.type === MMToolbarButton.Button.Save + anchors.centerIn: parent + width: 56 * __dp + height: width + radius: width / 2 + color: __style.transparentColor + border.color: __style.grassColor + border.width: 14 * __dp + } + } + Text { + id: text + + text: control.text + width: parent.width + color: __style.whiteColor + font: __style.t4 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 * __dp + horizontalAlignment: Text.AlignHCenter + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked() + } + } + + // Menu button + MMToolbarMenuButton { + width: control.width + height: __style.menuDrawerHeight + visible: control.isMenuButton + iconSource: control.iconSource + text: control.text + onClicked: control.clicked() + } +} diff --git a/app/qmlV2/component/MMToolbarLongButton.qml b/app/qmlV2/component/MMToolbarLongButton.qml new file mode 100644 index 000000000..301ada717 --- /dev/null +++ b/app/qmlV2/component/MMToolbarLongButton.qml @@ -0,0 +1,75 @@ +/*************************************************************************** + * * + * 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 + + required property var iconSource + required property string text + + readonly property double toolbarLongButtonWidth: 50 * __dp + readonly property double minimumToolbarLongButtonWidth: 200 * __dp + readonly property double maximumToolbarLongButtonWidth: 500 * __dp + + height: __style.toolbarHeight + + Button { + width: { + var w = parent.width - 10 * __dp + if(w < control.minimumToolbarLongButtonWidth) + return control.minimumToolbarLongButtonWidth + else if(w > control.maximumToolbarLongButtonWidth) + return control.maximumToolbarLongButtonWidth + return w + } + height: control.toolbarLongButtonWidth + anchors.centerIn: parent + + contentItem: Item { + width: parent.width + anchors.centerIn: parent + + Row { + id: row + + spacing: 5 * __dp + height: parent.height + anchors.centerIn: parent + + MMIcon { + source: control.iconSource + color: text.color + } + Text { + id: text + + text: control.text + color: __style.forestColor + font: __style.t3 + verticalAlignment: Text.AlignVCenter + topPadding: 10 * __dp + bottomPadding: 10 * __dp + } + } + } + + background: Rectangle { + color: __style.grassColor + radius: height / 2 + } + + onClicked: control.clicked() + } +} diff --git a/app/qmlV2/component/MMToolbarMenuButton.qml b/app/qmlV2/component/MMToolbarMenuButton.qml new file mode 100644 index 000000000..1a2353bf8 --- /dev/null +++ b/app/qmlV2/component/MMToolbarMenuButton.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 + + required property var iconSource + required property string text + + Item { + id: menuButton + + 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: 20 * __dp + MMIcon { + height: parent.height + color: __style.forestColor + source: control.iconSource + } + Text { + text: control.text + color: __style.forestColor + font: __style.t3 + verticalAlignment: Text.AlignVCenter + height: parent.height + } + } + + MouseArea { + anchors.fill: parent + onClicked: control.clicked() + } + } +} diff --git a/app/test/testlinks.h b/app/test/testlinks.h index 9bdeebdf7..5abc0d2a4 100644 --- a/app/test/testlinks.h +++ b/app/test/testlinks.h @@ -134,6 +134,11 @@ class TestLinks: public QObject _run( mHelp.whatsNewPostLink() ); } + void testChangelogLink() + { + _run( mHelp.changelogLink() ); + } + private: InputHelp mHelp; QNetworkAccessManager mManager; diff --git a/gallery/CMakeLists.txt b/gallery/CMakeLists.txt index d0f19d73b..68b9c010b 100644 --- a/gallery/CMakeLists.txt +++ b/gallery/CMakeLists.txt @@ -29,9 +29,9 @@ find_package( qt_standard_project_setup() qt_policy(SET QTP0001 NEW) -set(GALLERY_HDRS helper.h) +set(GALLERY_HDRS helper.h notificationmodel.h ../app/mmstyle.h) -set(GALLERY_SRCS helper.cpp) +set(GALLERY_SRCS helper.cpp notificationmodel.cpp main.cpp) if (IOS OR ANDROID) add_compile_definitions(MOBILE_OS) @@ -49,7 +49,6 @@ qt_add_executable( MerginMapsGallery ${GALLERY_SRCS} ${GALLERY_HDRS} - main.cpp qml.qrc fonts.qrc ../app/icons/icons.qrc @@ -69,7 +68,7 @@ qt_add_resources(fonts.qrc) set_target_properties( MerginMapsGallery - PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com + PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER uk.co.lutraconsulting.merginmaps.gallery MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} @@ -78,6 +77,7 @@ set_target_properties( ) target_link_libraries(MerginMapsGallery PRIVATE Qt6::Quick) +target_include_directories(MerginMapsGallery PRIVATE ../app) install( TARGETS MerginMapsGallery diff --git a/gallery/main.cpp b/gallery/main.cpp index 0055d1efa..f4e74460c 100644 --- a/gallery/main.cpp +++ b/gallery/main.cpp @@ -13,10 +13,11 @@ #ifdef DESKTOP_OS #include "hotreload.h" #endif +#include "mmstyle.h" #include "helper.h" #include #include - +#include "notificationmodel.h" int main( int argc, char *argv[] ) { QGuiApplication app( argc, argv ); @@ -30,9 +31,15 @@ int main( int argc, char *argv[] ) engine.rootContext()->setContextProperty( "_hotReload", &hotReload ); #endif + qreal dp = Helper::calculateDpRatio(); + MMStyle style( dp ); + NotificationModel notificationModel; + + engine.rootContext()->setContextProperty( "notificationModel", ¬ificationModel ); // path to local wrapper pages engine.rootContext()->setContextProperty( "_qmlWrapperPath", QGuiApplication::applicationDirPath() + "/HotReload/qml/pages/" ); - engine.rootContext()->setContextProperty( "__dp", Helper::calculateDpRatio() ); + engine.rootContext()->setContextProperty( "__dp", dp ); + engine.rootContext()->setContextProperty( "__style", &style ); engine.rootContext()->setContextProperty( "__isMobile", Helper::isMobile() ); QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, diff --git a/gallery/notificationmodel.cpp b/gallery/notificationmodel.cpp new file mode 100644 index 000000000..191e8773f --- /dev/null +++ b/gallery/notificationmodel.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "notificationmodel.h" +#include + +Notification::Notification( uint id, const QString &message, uint interval, NotificationType::MessageType type = NotificationType::Information, NotificationType::IconType icon = NotificationType::None ) +{ + mId = id; + mMessage = message; + mInterval = interval; + mType = type; + mIcon = icon; +} + +NotificationModel::NotificationModel( QObject *parent ) : QAbstractListModel{parent} +{ + qmlRegisterUncreatableType( "notificationType", 1, 0, "NotificationType", "Not creatable as it is an enum type" ); + + mTimer = new QTimer( this ); + connect( mTimer, &QTimer::timeout, this, &NotificationModel::timerFired ); + mTimer->start( 1000 ); +} + +NotificationModel::~NotificationModel() +{ + mTimer->stop(); + delete mTimer; +} + +QHash NotificationModel::roleNames() const +{ + return + { + { IdRole, "id" }, + { MessageRole, "message" }, + { TypeRole, "type" }, + { IconRole, "icon" } + }; +} + +int NotificationModel::rowCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return mNotifications.size(); +} + +QVariant NotificationModel::data( const QModelIndex &index, int role ) const +{ + if ( !hasIndex( index.row(), index.column(), index.parent() ) ) + return {}; + + Notification notification = mNotifications.at( index.row() ); + if ( role == IdRole ) return notification.id(); + if ( role == MessageRole ) return notification.message(); + if ( role == TypeRole ) return notification.type(); + if ( role == IconRole ) return notification.icon(); + + return {}; +} + +// remove item by message +void NotificationModel::remove( uint id ) +{ + for ( int i = 0; i < mNotifications.count(); i++ ) + { + if ( mNotifications[i].id() == id ) + { + beginRemoveRows( QModelIndex(), i, i ); + mNotifications.removeAt( i ); + endRemoveRows(); + + emit dataChanged( createIndex( 0, 0 ), createIndex( rowCount(), 0 ) ); // refresh whole model + emit rowCountChanged(); + return; + } + } +} + +// add new unique message with interval +void NotificationModel::add( const QString &message, uint interval, NotificationType::MessageType type = NotificationType::Information, NotificationType::IconType icon = NotificationType::None ) +{ + for ( Notification ¬ification : mNotifications ) + { + if ( notification.message() == message ) + return; + } + + beginInsertRows( QModelIndex(), rowCount(), rowCount() ); + mNotifications << Notification{ nextId(), message, interval, type, icon }; + endInsertRows(); + + emit rowCountChanged(); +} + +// check for auto removing notification +void NotificationModel::timerFired() +{ + for ( Notification ¬ification : mNotifications ) + { + if ( notification.isRemovableAfterDecrement() ) + remove( notification.id() ); + } +} + + diff --git a/gallery/notificationmodel.h b/gallery/notificationmodel.h new file mode 100644 index 000000000..43379d839 --- /dev/null +++ b/gallery/notificationmodel.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef NOTIFICATIONMODEL_H +#define NOTIFICATIONMODEL_H + +#include "qqmlintegration.h" +#include +#include + +class NotificationType +{ + Q_GADGET + public: + + enum MessageType + { + Information, + Success, + Warning, + Error + }; + Q_ENUM( MessageType ) + + enum IconType + { + None, + Waiting, + Check + }; + Q_ENUM( IconType ) + + private: + explicit NotificationType(); +}; + +class Notification +{ + Q_GADGET + + public: + Notification( uint id, const QString &message, uint interval, NotificationType::MessageType type, NotificationType::IconType icon ); + uint id() { return mId; } + QString message() { return mMessage; } + NotificationType::MessageType type() { return mType; } + NotificationType::IconType icon() { return mIcon; } + bool isRemovableAfterDecrement() { return ( mInterval-- <= 0 ); } + + private: + uint mId; + QString mMessage; + uint mInterval; // [seconds] + NotificationType::MessageType mType; + NotificationType::IconType mIcon; +}; + +class NotificationModel : public QAbstractListModel +{ + Q_OBJECT + + public: + enum MyRoles + { + IdRole = Qt::UserRole + 1, MessageRole, TypeRole, IconRole + }; + Q_ENUM( MyRoles ) + + NotificationModel( QObject *parent = nullptr ); + ~NotificationModel(); + + QHash roleNames() const override; + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + + Q_PROPERTY( int rowCount READ rowCount NOTIFY rowCountChanged ); + Q_INVOKABLE void remove( uint id ); + Q_INVOKABLE void add( const QString &message, uint interval, NotificationType::MessageType type, NotificationType::IconType icon ); + + private: + uint nextId() { static uint id = 0; return id++; } + void timerFired(); + + signals: + void rowCountChanged(); + + private: + QList mNotifications; + QTimer *mTimer; +}; + +#endif // NOTIFICATIONMODEL_H diff --git a/gallery/qml.qrc b/gallery/qml.qrc index 3e165441a..b24a0a80c 100644 --- a/gallery/qml.qrc +++ b/gallery/qml.qrc @@ -5,7 +5,6 @@ qml/pages/InputsPage.qml qml/pages/ButtonsPage.qml ../app/qmlV2/component/MMInput.qml - ../app/qmlV2/Style.js ../app/qmlV2/component/MMButton.qml ../app/qmlV2/component/MMLinkButton.qml ../app/qmlV2/component/MMLink.qml @@ -19,10 +18,25 @@ ../app/qmlV2/component/MMCheckBox.qml ../app/qmlV2/component/MMRadioButton.qml ../app/qmlV2/component/MMSwitch.qml + qml/pages/NotificationPage.qml + ../app/qmlV2/component/MMNotification.qml + ../app/qmlV2/component/MMNotificationView.qml ../app/qmlV2/component/MMDrawer.qml qml/pages/DrawerPage.qml qml/pages/ChecksPage.qml ../app/qmlV2/component/MMComponent_reachedDataLimit.qml ../app/qmlV2/component/MMProgressBar.qml + qml/pages/MapPage.qml + ../app/qmlV2/component/MMMapButton.qml + ../app/qmlV2/component/MMShadow.qml + ../app/qmlV2/component/MMMapLabel.qml + ../app/qmlV2/component/MMPasswordInput.qml + ../app/qmlV2/component/MMButtonInput.qml + ../app/qmlV2/component/MMToolbarButton.qml + ../app/qmlV2/component/MMToolbar.qml + qml/pages/ToolbarPage.qml + ../app/qmlV2/component/MMMenuDrawer.qml + ../app/qmlV2/component/MMToolbarMenuButton.qml + ../app/qmlV2/component/MMToolbarLongButton.qml diff --git a/gallery/qml/Main.qml b/gallery/qml/Main.qml index 1859be814..738e82116 100644 --- a/gallery/qml/Main.qml +++ b/gallery/qml/Main.qml @@ -124,10 +124,22 @@ ApplicationWindow { title: "Checks" source: "ChecksPage.qml" } + ListElement { + title: "Notifications" + source: "NotificationPage.qml" + } ListElement { title: "Drawers" source: "DrawerPage.qml" } + ListElement { + title: "Map" + source: "MapPage.qml" + } + ListElement { + title: "Toolbars" + source: "ToolbarPage.qml" + } } ScrollIndicator.vertical: ScrollIndicator {} diff --git a/gallery/qml/pages/DrawerPage.qml b/gallery/qml/pages/DrawerPage.qml index 6111fedbe..267c413cb 100644 --- a/gallery/qml/pages/DrawerPage.qml +++ b/gallery/qml/pages/DrawerPage.qml @@ -12,7 +12,7 @@ import QtQuick.Controls import QtQuick.Controls.Basic import "../../app/qmlV2/component" -import "../../app/qmlV2/Style.js" as Style +import "../../app/qmlV2" Page { id: pane @@ -38,7 +38,7 @@ Page { MMDrawer { id: drawer1 - picture: Style.uploadImage + picture: __style.uploadImage title: "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" @@ -51,7 +51,7 @@ Page { MMDrawer { id: drawer2 - picture: Style.ReachedDataLimitImage + picture: __style.reachedDataLimitImage title: "You have reached a data limit" primaryButton: "Manage Subscription" specialComponent: component.comp @@ -72,7 +72,7 @@ Page { MMDrawer { id: drawer3 - picture: Style.uploadImage + picture: __style.uploadImage title: "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" diff --git a/gallery/qml/pages/InputsPage.qml b/gallery/qml/pages/InputsPage.qml index d873094a4..30cfb50aa 100644 --- a/gallery/qml/pages/InputsPage.qml +++ b/gallery/qml/pages/InputsPage.qml @@ -12,78 +12,109 @@ import QtQuick.Controls import QtQuick.Controls.Basic import "../../app/qmlV2/component" +import "../../app/qmlV2/" -Column { - padding: 20 - spacing: 20 +ScrollView { + Column { + padding: 20 + spacing: 20 - GroupBox { - title: "MMInput" - background: Rectangle { - color: "lightGray" - border.color: "gray" - } - label: Label { - color: "black" - text: parent.title - padding: 5 - } - - Column { - spacing: 10 - anchors.fill: parent - MMInput { - placeholderText: "Place holder" + GroupBox { + title: "MMInput" + background: Rectangle { + color: "lightGray" + border.color: "gray" } - MMInput { - text: "Disabled" - enabled: false + label: Label { + color: "black" + text: parent.title + padding: 5 } + Column { - TextInput { text: "type: MMInput.Type.Search" } + spacing: 10 + anchors.fill: parent MMInput { - type: MMInput.Type.Search - placeholderText: "Search" + title: "Title" + placeholderText: "Place holder" } - } - Column { - TextInput { text: "type: MMInput.Type.Calendar" } - TextInput { text: "warningMsg: ..." } MMInput { - type: MMInput.Type.Calendar - text: "Calendar" - warningMsg: "Would you like to be so kind and select a date please?" + title: "Title" + text: "Disabled" + enabled: false + } + Column { + MMInput { + title: "Search ..." + placeholderText: "Search" + iconSource: __style.searchIcon + } } + Column { + MMInput { + text: "Calendar" + iconSource: __style.calendarIcon + warningMsg: "Would you like to be so kind and select a date please?" + } + } + } + } + + GroupBox { + title: "MMPasswordInput" + background: Rectangle { + color: "lightGray" + border.color: "gray" } + label: Label { + color: "black" + text: parent.title + padding: 5 + } + Column { - TextInput { text: "type: MMInput.Type.Password" } - TextInput { text: "errorMsg: ..." } - MMInput { - type: MMInput.Type.Password + spacing: 10 + anchors.fill: parent + MMPasswordInput { + title: "Username password" text: "Password" - errorMsg: "Password must contain at least 6 characters\nMinimum 1 number\nAnd 1 special character" + 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" } - } - Column { - TextInput { text: "type: MMInput.Type.Scan" } - MMInput { - type: MMInput.Type.Scan - text: "QR Code" + MMPasswordInput { + text: "Password" + regexp: '(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{6,})' + enabled: false } } - Column { - TextInput { text: "type: MMInput.Type.CopyButton" } - MMInput { - type: MMInput.Type.CopyButton - text: "Copy me" - } + } + + GroupBox { + title: "MMButtonInput" + background: Rectangle { + color: "lightGray" + border.color: "gray" + } + label: Label { + color: "black" + text: parent.title + padding: 5 } + Column { - TextInput { text: "type: MMInput.Type.CopyButton" } - MMInput { - type: MMInput.Type.CopyButton - text: "Copy me" + spacing: 10 + anchors.fill: parent + MMButtonInput { + title: "Title" + text: "Copy text to clipboard" + buttonText: "Copy" + onClicked: { textToClipboard(); console.log("Text in clipboard") } + } + MMButtonInput { + text: "Send" + buttonText: "Send" enabled: false + onClicked: console.log("Clicked") } } } diff --git a/gallery/qml/pages/MapPage.qml b/gallery/qml/pages/MapPage.qml new file mode 100644 index 000000000..88dde5862 --- /dev/null +++ b/gallery/qml/pages/MapPage.qml @@ -0,0 +1,119 @@ +/*************************************************************************** + * * + * 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 QtLocation +import QtPositioning + +import "../../app/qmlV2/component" +import "../../app/qmlV2/" + +Page { + id: pane + + Plugin { + id: mapPlugin + name: "osm" + } + + Map { + id: map + + anchors.fill: parent + plugin: mapPlugin + center: QtPositioning.coordinate(48.72, 21.25) // KE + zoomLevel: 12 + property geoCoordinate startCentroid + + PinchHandler { + id: pinch + + target: null + onActiveChanged: if (active) { + map.startCentroid = map.toCoordinate(pinch.centroid.position, false) + } + onScaleChanged: (delta) => { + map.zoomLevel += Math.log2(delta) + map.alignCoordinateToPoint(map.startCentroid, pinch.centroid.position) + } + onRotationChanged: (delta) => { + map.bearing -= delta + map.alignCoordinateToPoint(map.startCentroid, pinch.centroid.position) + } + grabPermissions: PointerHandler.TakeOverForbidden + } + + WheelHandler { + acceptedDevices: Qt.platform.pluginName === "cocoa" || Qt.platform.pluginName === "wayland" + ? PointerDevice.Mouse | PointerDevice.TouchPad + : PointerDevice.Mouse + rotationScale: 1/120 + property: "zoomLevel" + } + + DragHandler { + target: null + onTranslationChanged: (delta) => map.pan(-delta.x, -delta.y) + } + } + + Rectangle { + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + height: 30 + color: "white" + + Text { + anchors.centerIn: parent + text: map.center + "\tzoom: " + map.zoomLevel.toFixed(2) + } + } + + Column { + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.bottomMargin: 50 + spacing: 20 + + MMMapButton { + iconSource: __style.arrowLinkRightIcon + onClicked: console.log("Map button clicked") + } + + MMMapButton { + iconSource: __style.searchIcon + onClicked: console.log("Map button clicked") + } + } + + MMMapLabel { + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: 20 + anchors.bottomMargin: 120 + + text: "20.0 m" + iconSource: __style.checkmarkIcon + } + + MMMapLabel { + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: 20 + anchors.bottomMargin: 50 + + text: "20.0 m" + } +} diff --git a/gallery/qml/pages/NotificationPage.qml b/gallery/qml/pages/NotificationPage.qml new file mode 100644 index 000000000..49bb14e2b --- /dev/null +++ b/gallery/qml/pages/NotificationPage.qml @@ -0,0 +1,68 @@ +/*************************************************************************** + * * + * 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/qmlV2/component" +import notificationType 1.0 + +Page { + id: pane + + Rectangle { + anchors.fill: parent + color: "white" + } + + Column { + width: parent.width + spacing: 20 + anchors.centerIn: parent + + MMButtonInput { + buttonText: "Send" + anchors.horizontalCenter: parent.horizontalCenter + placeholderText: "Write an informative message" + onClicked: { notificationModel.add(text, 60, NotificationType.Information, NotificationType.None); text = "" } + } + MMButtonInput { + buttonText: "Send" + anchors.horizontalCenter: parent.horizontalCenter + placeholderText: "Write a success message" + onClicked: { notificationModel.add(text, 60, NotificationType.Success, NotificationType.Check); text = "" } + } + MMButtonInput { + buttonText: "Send" + anchors.horizontalCenter: parent.horizontalCenter + placeholderText: "Write a warning message" + onClicked: { notificationModel.add(text, 60, NotificationType.Warning, NotificationType.Waiting); text = "" } + } + MMButtonInput { + buttonText: "Send" + anchors.horizontalCenter: parent.horizontalCenter + placeholderText: "Write an error message" + onClicked: { notificationModel.add(text, 60, NotificationType.Error, NotificationType.None); text = "" } + } + MMButtonInput { + buttonText: "Send" + anchors.horizontalCenter: parent.horizontalCenter + text: "Stojí, stojí mohyla, Na mohyle zlá chvíľa, Na mohyle tŕnie chrastie A v tom tŕní, chrastí rastie, Rastie, kvety rozvíja Jedna žltá ľalia. Tá ľalia smutno vzdychá: „Hlávku moju tŕnie pichá A nožičky oheň páli – Pomôžte mi v mojom žiali!“ " + onClicked: { notificationModel.add(text, 60, NotificationType.Information, NotificationType.None); text = "" } + } + Text { + text: "Note: Notification will be removed in 1 minute" + anchors.horizontalCenter: parent.horizontalCenter + color: "green" + } + } + + MMNotificationView {} +} diff --git a/gallery/qml/pages/ToolbarPage.qml b/gallery/qml/pages/ToolbarPage.qml new file mode 100644 index 000000000..d460725bd --- /dev/null +++ b/gallery/qml/pages/ToolbarPage.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 "../../app/qmlV2/component" +import "../../app/qmlV2/" + +Page { + id: pane + + MMToolbar { + model: ObjectModel { + MMToolbarLongButton { text: "Long button"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + } + anchors.bottomMargin: 360 * __dp + } + + MMToolbar { + model: ObjectModel { + MMToolbarButton { text: "aaa"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "bbb"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + } + anchors.bottomMargin: 240 * __dp + } + + MMToolbar { + model: ObjectModel { + MMToolbarButton { text: "1/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "2/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "3/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "4/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "5/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "6/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "7/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: "8/8"; iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + } + anchors.bottomMargin: 120 * __dp + } + + MMToolbar { + model: ObjectModel { + MMToolbarButton { text: qsTr("Delete"); iconSource: __style.deleteIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: qsTr("Edit Geometry"); iconSource: __style.editIcon; onClicked: console.log("tapped "+text) } + MMToolbarButton { text: qsTr("Save"); iconSource: __style.doneIcon; type: MMToolbarButton.Button.Save; onClicked: console.log("tapped "+text) } + } + } +}