diff --git a/app/main.cpp b/app/main.cpp index 00eceb9de..b226586d3 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -454,6 +454,11 @@ int main( int argc, char *argv[] ) init_qgis( appBundleDir ); +#ifdef ANDROID + // See https://bugreports.qt.io/browse/QTBUG-86982 -> fix to make the predictive text disabled on Android + qputenv( "QT_ANDROID_ENABLE_WORKAROUND_TO_DISABLE_PREDICTIVE_TEXT", "1" ); +#endif + // AppSettings has to be initialized after QGIS app init (because of correct reading/writing QSettings). AppSettings as; diff --git a/app/mmstyle.h b/app/mmstyle.h index bf116ec3d..0b548ad7f 100644 --- a/app/mmstyle.h +++ b/app/mmstyle.h @@ -83,8 +83,6 @@ class MMStyle: public QObject 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 ) Q_PROPERTY( QColor snappingColor READ snappingColor CONSTANT ) @@ -286,6 +284,7 @@ class MMStyle: public QObject // Other Q_PROPERTY( double row1 READ number1 CONSTANT ) + Q_PROPERTY( double row4 READ number4 CONSTANT ) Q_PROPERTY( double row24 READ number24 CONSTANT ) Q_PROPERTY( double row36 READ number36 CONSTANT ) Q_PROPERTY( double row40 READ number40 CONSTANT ) @@ -297,6 +296,9 @@ class MMStyle: public QObject Q_PROPERTY( double row67 READ number67 CONSTANT ) Q_PROPERTY( double row80 READ number80 CONSTANT ) Q_PROPERTY( double row114 READ number114 CONSTANT ) + Q_PROPERTY( double row120 READ number120 CONSTANT ) + Q_PROPERTY( double row160 READ number160 CONSTANT ) + Q_PROPERTY( double radius2 READ number2 CONSTANT ) Q_PROPERTY( double radius6 READ number6 CONSTANT ) Q_PROPERTY( double radius8 READ number8 CONSTANT ) Q_PROPERTY( double radius12 READ number12 CONSTANT ) @@ -304,6 +306,8 @@ class MMStyle: public QObject Q_PROPERTY( double radius20 READ number20 CONSTANT ) Q_PROPERTY( double radius30 READ number30 CONSTANT ) Q_PROPERTY( double radius40 READ number40 CONSTANT ) + Q_PROPERTY( double width1 READ number1 CONSTANT ) + Q_PROPERTY( double width2 READ number2 CONSTANT ) Q_PROPERTY( double scrollVelocityAndroid READ scrollVelocityAndroid CONSTANT ) // [px/s] scrolling on Android devices is too slow by default // Breakpoint we use in some screens to differentiate mobile landscape @@ -366,8 +370,6 @@ class MMStyle: public QObject QColor informativeColor() {return QColor::fromString( "#BEDAF0" );} QColor snappingColor() {return QColor::fromString( "#BD74FF" );} - QColor nightAlphaColor() {return QColor::fromString( "#AA12181F" );} - QColor errorBgInputColor() {return QColor::fromString( "#FEFAF9" );} QColor shadowColor() {return QColor::fromString( "#66777777" );} QUrl splitGeometryIcon() {return QUrl( "qrc:/SplitGeometry.svg" );} @@ -675,6 +677,7 @@ class MMStyle: public QObject double number148() {return 148 * mDp;} double number149() {return 149 * mDp;} double number150() {return 150 * mDp;} + double number160() {return 160 * mDp;} double number250() {return 250 * mDp;} double number400() {return 400 * mDp;} double number720() {return 720 * mDp;} diff --git a/app/qml/CMakeLists.txt b/app/qml/CMakeLists.txt index 425c41ed3..4f6a37960 100644 --- a/app/qml/CMakeLists.txt +++ b/app/qml/CMakeLists.txt @@ -34,15 +34,12 @@ set(MM_QML components/MMListSpacer.qml components/MMBusyIndicator.qml components/MMMessage.qml - components/MMMorePhoto.qml components/MMNotification.qml components/MMNotificationView.qml components/MMPage.qml components/MMPageHeader.qml components/MMPopup.qml components/MMPhoto.qml - components/MMPhotoAttachment.qml - components/MMPhotoPreview.qml components/MMProgressBar.qml components/MMRadioButton.qml components/MMRoundButton.qml @@ -52,6 +49,8 @@ set(MM_QML components/MMText.qml components/MMToolbar.qml components/MMToolbarButton.qml + components/private/MMBaseInput.qml + components/private/MMBaseSingleLineInput.qml components/private/MMToolbarLongButton.qml components/private/MMToolbarShortButton.qml dialogs/MMCloseAccountDialog.qml @@ -91,6 +90,8 @@ set(MM_QML form/components/calendar/MMMonthGrid.qml form/components/calendar/MMTimeTumbler.qml form/components/calendar/MMTumbler.qml + form/components/photo/MMPhotoAttachment.qml + form/components/photo/MMPhotoPreview.qml form/editors/MMFormCalendarEditor.qml form/editors/MMFormComboboxBaseEditor.qml form/editors/MMFormGalleryEditor.qml @@ -114,12 +115,10 @@ set(MM_QML gps/MMPositionProviderPage.qml gps/MMStakeoutDrawer.qml gps/components/MMGpsDataText.qml - inputs/MMBaseInput.qml inputs/MMComboboxInput.qml inputs/MMPasswordInput.qml inputs/MMSearchInput.qml inputs/MMTextInput.qml - inputs/MMTextWithButtonInput.qml inputs/MMSwitchInput.qml layers/MMFeaturesListPage.qml layers/MMLayerDetailPage.qml diff --git a/app/qml/account/MMHowYouFoundUsPage.qml b/app/qml/account/MMHowYouFoundUsPage.qml index 4c5583f9d..fa7f89690 100644 --- a/app/qml/account/MMHowYouFoundUsPage.qml +++ b/app/qml/account/MMHowYouFoundUsPage.qml @@ -224,7 +224,7 @@ MMPage { onTextChanged: root.selectedText = text - Component.onCompleted: textFieldComponent.forceActiveFocus() + Component.onCompleted: textField.forceActiveFocus() } } } diff --git a/app/qml/account/MMLoginPage.qml b/app/qml/account/MMLoginPage.qml index 5929fc0ad..95e29dc1e 100644 --- a/app/qml/account/MMLoginPage.qml +++ b/app/qml/account/MMLoginPage.qml @@ -85,7 +85,7 @@ MMPage { width: parent.width title: qsTr( "Email or username" ) - textFieldComponent.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + textField.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhEmailCharactersOnly } MMPasswordInput { @@ -200,10 +200,10 @@ MMPage { title: qsTr( "Server address" ) - bgColor: __style.lightGreenColor + textFieldBackground.color: __style.lightGreenColor text: root.apiRoot - textFieldComponent.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + textField.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhUrlCharactersOnly } MMListSpacer { height: __style.spacing40 } diff --git a/app/qml/account/MMSignUpPage.qml b/app/qml/account/MMSignUpPage.qml index e1de1645f..cc5fd028b 100644 --- a/app/qml/account/MMSignUpPage.qml +++ b/app/qml/account/MMSignUpPage.qml @@ -72,7 +72,7 @@ MMPage { width: parent.width title: qsTr( "Username" ) - textFieldComponent.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + textField.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase } MMTextInput { @@ -81,7 +81,7 @@ MMPage { width: parent.width title: qsTr( "Email" ) - textFieldComponent.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + textField.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhEmailCharactersOnly } MMPasswordInput { diff --git a/app/qml/account/MMSwitchWorkspacePage.qml b/app/qml/account/MMSwitchWorkspacePage.qml index 14164749a..eb12841b1 100644 --- a/app/qml/account/MMSwitchWorkspacePage.qml +++ b/app/qml/account/MMSwitchWorkspacePage.qml @@ -60,7 +60,7 @@ MMComponents.MMPage { placeholderText: qsTr( "Search" ) + "..." - onSearchTextChanged: function( searchText ) { root.searchTextChanged( searchText ) } + onSearchTextChanged: root.searchTextChanged( searchBar.searchText ) } MMComponents.MMScrollView { diff --git a/app/qml/account/MMWhichIndustryPage.qml b/app/qml/account/MMWhichIndustryPage.qml index b0c82786e..e8fb981e3 100644 --- a/app/qml/account/MMWhichIndustryPage.qml +++ b/app/qml/account/MMWhichIndustryPage.qml @@ -211,9 +211,9 @@ MMPage { title: qsTr( "Industry" ) placeholderText: internal.specifyIndustryText - onTextChanged: root.selectedText = text + onTextEdited: ( text ) => root.selectedText = text - Component.onCompleted: textFieldComponent.forceActiveFocus() + Component.onCompleted: textField.forceActiveFocus() } } } diff --git a/app/qml/components/MMCheckBox.qml b/app/qml/components/MMCheckBox.qml index a7ffebd77..e2f49f9f0 100644 --- a/app/qml/components/MMCheckBox.qml +++ b/app/qml/components/MMCheckBox.qml @@ -18,6 +18,8 @@ CheckBox { property bool hasError: false + property color emptyStateColor: __style.transparentColor + implicitHeight: Math.max( textContent.implicitHeight, indicatorContent.height ) topPadding: __style.margin4 @@ -35,7 +37,7 @@ CheckBox { radius: __style.radius6 - color: ( root.checked && root.enabled ) ? __style.grassColor: __style.transparentColor + color: ( root.checked && root.enabled ) ? __style.grassColor : emptyStateColor border.color: { if ( enabled ) { diff --git a/app/qml/components/MMListMultiselectDrawer.qml b/app/qml/components/MMListMultiselectDrawer.qml index 2ec833eab..709172540 100644 --- a/app/qml/components/MMListMultiselectDrawer.qml +++ b/app/qml/components/MMListMultiselectDrawer.qml @@ -52,10 +52,11 @@ MMDrawer { placeholderText: qsTr( "Search" ) - bgColor: __style.lightGreenColor + textFieldBackground.color: __style.lightGreenColor + visible: root.withSearch - onSearchTextChanged: ( text ) => root.searchTextChanged( text ) + onSearchTextChanged: root.searchTextChanged( searchBar.searchText ) } MMListSpacer { id: searchBarSpacer; height: __style.spacing20; visible: root.withSearch } diff --git a/app/qml/components/MMMorePhoto.qml b/app/qml/components/MMMorePhoto.qml deleted file mode 100644 index 7a0fe644b..000000000 --- a/app/qml/components/MMMorePhoto.qml +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -import QtQuick -import QtQuick.Controls -import Qt5Compat.GraphicalEffects - -Row { - id: control - - required property url photoUrl - property int hiddenPhotoCount: 0 - property int space: 0 - - signal clicked() - - // left space - Item { width: control.space; height: 1 } - - Image { - id: image - - width: control.width - control.space - height: width - - source: control.photoUrl - asynchronous: true - autoTransform: true - layer.enabled: true - layer { - effect: OpacityMask { - maskSource: Item { - width: image.width - height: width - Rectangle { - anchors.centerIn: parent - width: image.width - height: parent.height - radius: 20 * __dp - } - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: control.clicked() - } - - Rectangle { - anchors.centerIn: parent - width: image.width - height: parent.height - - radius: 20 * __dp - color: __style.transparentColor - border.color: __style.forestColor - border.width: 1 * __dp - } - - Image { - id: bluredImage - - anchors.fill: parent - source: image.source - - asynchronous: true - layer.enabled: true - - layer { - effect: FastBlur { - anchors.fill: bluredImage - source: bluredImage - radius: 32 - } - } - } - - Column { - anchors.centerIn: parent - - Image { - source: __style.morePhotosIcon - anchors.horizontalCenter: parent.horizontalCenter - } - - Text { - height: 26 * __dp - font: __style.t4 - text: qsTr("+%1 more").arg(control.hiddenPhotoCount) - color: __style.polarColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - - onStatusChanged: { - if (status === Image.Error) { - console.error("MMMorePhoto: Error loading image"); - } - } - } -} diff --git a/app/qml/components/MMSwitch.qml b/app/qml/components/MMSwitch.qml index 5ecca21ab..ae9d1d181 100644 --- a/app/qml/components/MMSwitch.qml +++ b/app/qml/components/MMSwitch.qml @@ -13,10 +13,9 @@ import QtQuick.Controls Switch { id: root + property color checkedBgColor: enabled ? __style.grassColor : __style.mediumGreenColor property color uncheckedBgColor: __style.polarColor - property color checkedBgColor: __style.grassColor - property color disabledFgColor: __style.mediumGreenColor - property color enabledFgColor: __style.forestColor + property color handleColor: enabled ? __style.forestColor : __style.mediumGreyColor topPadding: 0 rightPadding: 0 @@ -26,7 +25,7 @@ Switch { contentItem: Text { text: root.text font: __style.p5 - color: root.enabled ? root.enabledFgColor : root.disabledFgColor + color: root.handleColor verticalAlignment: Text.AlignVCenter leftPadding: root.indicator.width + ( text ? root.spacing : 0 ) } @@ -37,7 +36,6 @@ Switch { implicitWidth: 48 * __dp implicitHeight: 28 * __dp x: root.leftPadding - y: parent.height / 2 - height / 2 radius: implicitHeight / 2 color: root.checked ? root.checkedBgColor : root.uncheckedBgColor @@ -46,7 +44,7 @@ Switch { width: 20 * __dp height: width radius: width / 2 - color: root.enabled ? root.enabledFgColor : root.disabledFgColor + color: root.handleColor anchors.verticalCenter: parent.verticalCenter } } diff --git a/app/qml/components/private/MMBaseInput.qml b/app/qml/components/private/MMBaseInput.qml new file mode 100644 index 000000000..b25374702 --- /dev/null +++ b/app/qml/components/private/MMBaseInput.qml @@ -0,0 +1,211 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick +import QtQuick.Layouts + +import "../" as MMComponents + +/** + * MMBaseInput is a base class for all inputs in the app. + * Inputs and form editors are derived from this component. + * + * Private class, do not use in the app. + */ + +Item { + id: root + + property alias title: titleText.text + + property bool hasCheckbox: false + property alias checkboxChecked: checkbox.checked + + property bool readOnly: false + + property string errorMsg: "" + property string warningMsg: "" + + property alias inputContent: contentGroup.children + + property alias editState: editStateGroup.state + property alias validationState: validationStateGroup.state + + StateGroup { + id: editStateGroup + + states: [ + State { + name: "enabled" + when: enabled && !readOnly + }, + State { + name: "disabled" + when: !enabled + }, + State { + name: "readOnly" + when: enabled && readOnly + } + ] + + state: "enabled" + } + + StateGroup { + id: validationStateGroup + + states: [ + State { + name: "valid" + when: !warningMsg && !errorMsg + }, + State { + name: "error" + when: errorMsg + }, + State { + name: "warning" + when: warningMsg && !errorMsg + } + ] + + state: "valid" + } + + implicitHeight: rootGroup.implicitHeight + + Column { + id: rootGroup + + width: parent.width + + spacing: __style.margin4 + + Item { + // checkbox and title div + + width: parent.width + implicitHeight: titleRowGroup.implicitHeight + + RowLayout { + id: titleRowGroup + + width: parent.width + + // Checkbox has some padding to the right so we do not need spacing. + // Once we refactor the checkbox, we need to add spacing here. + spacing: 0 + + MMComponents.MMCheckBox { + id: checkbox + + small: true + visible: root.hasCheckbox && root.editState === "enabled" + + emptyStateColor: __style.polarColor + checked: root.checkboxChecked + + MouseArea { + anchors { + fill: parent + margins: -__style.margin16 + } + + onClicked: function( mouse ) { + mouse.accepted = true + checkbox.toggle() + } + } + + } + + MMComponents.MMText { + id: titleText + + text: root.title + font: __style.p6 + + Layout.fillWidth: true + + wrapMode: Text.Wrap + maximumLineCount: 10 + + MouseArea { + width: parent.contentWidth + 2 * __style.margin12 + height: parent.contentHeight + + x: -__style.margin12 + + onClicked: function( mouse ) { + mouse.accepted = true + + if ( checkbox.visible ) { + checkbox.toggle() + } + } + } + } + } + } + + Item { + id: contentGroup + + width: parent.width + height: childrenRect.height + } + + Item { + // validation messages + + width: parent.width + height: validationMessagegroup.implicitHeight + + visible: root.validationState !== "valid" && root.editState === "enabled" + + RowLayout { + id: validationMessagegroup + + width: parent.width + + MMComponents.MMIcon { + source: __style.errorCircleIcon + size: __style.icon16 + color: { + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + return __style.forestColor + } + } + + MMComponents.MMText { + Layout.fillWidth: true + + text: { + if ( root.validationState === "error" ) return root.errorMsg + if ( root.validationState === "warning" ) return root.warningMsg + return "" + } + color: { + if ( root.validationState === "error" ) return __style.grapeColor + if ( root.validationState === "warning" ) return __style.earthColor + return __style.forestColor + } + + font: __style.t4 + + + wrapMode: Text.Wrap + maximumLineCount: 2 + } + } + } + } +} diff --git a/app/qml/components/private/MMBaseSingleLineInput.qml b/app/qml/components/private/MMBaseSingleLineInput.qml new file mode 100644 index 000000000..e7fb44676 --- /dev/null +++ b/app/qml/components/private/MMBaseSingleLineInput.qml @@ -0,0 +1,216 @@ +/*************************************************************************** + * * + * 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 + +// To ignore the warning "The current style does not support customization" +// see from https://stackoverflow.com/questions/76625756/the-current-style-does-not-support-customization-of-this-control +import QtQuick.Controls.Basic + +/** + * MMBaseSingleLineInput serves as a base class for all inputs that can benefit from + * predefined textfield and/or left/right icons (actions). + * + * Private class, do not use standalone in the app. + */ + +MMBaseInput { + id: root + + property alias text: textFieldControl.text + property alias placeholderText: textFieldControl.placeholderText + + property alias textField: textFieldControl + property alias textFieldBackground: textFieldBackgroundGroup + + property alias leftContent: leftContentGroup.children + property alias rightContent: rightContentGroup.children + property alias leftContentMouseArea: leftContentMouseAreaGroup + property alias rightContentMouseArea: rightContentMouseAreaGroup + + property bool rightContentVisible: rightContentGroup.children.length > 0 + property bool leftContentVisible: leftContentGroup.children.length > 0 + + // IconColor is not used in this file directly, but derived components can use it + // as precalculated color for icons to avoid repeating the same text over and over. + property color iconColor: { + if ( root.editState !== "enabled" ) return __style.mediumGreyColor + if ( root.validationState === "error" ) return __style.grapeColor + if ( root.validationState === "warning" ) return __style.earthColor + return __style.forestColor + } + + signal textEdited( string text ) + signal textClicked() + signal leftContentClicked() + signal rightContentClicked() + + inputContent: Rectangle { + id: textFieldBackgroundGroup + + width: parent.width + height: __style.row50 + + color: { + if ( root.editState !== "enabled" ) return __style.polarColor + if ( root.validationState === "error" ) return __style.negativeUltraLightColor + if ( root.validationState === "warning" ) return __style.negativeUltraLightColor + + return __style.polarColor + } + + border.width: { + if ( root.validationState === "error" ) return __style.width2 + if ( root.validationState === "warning" ) return __style.width2 + if ( textFieldControl.activeFocus ) return __style.width2 + if ( textFieldControl.hovered ) return __style.width1 + return 0 + } + + border.color: { + if ( root.editState !== "enabled" ) return __style.polarColor + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + if ( textFieldControl.activeFocus ) return __style.forestColor + if ( textFieldControl.hovered ) return __style.forestColor + + return __style.polarColor + } + + radius: __style.radius12 + + RowLayout { + anchors .fill: parent + + spacing: __style.margin12 + + Item { + id: leftContentGroupContainer + + Layout.preferredHeight: leftContentGroup.height + Layout.preferredWidth: leftContentGroup.width + Layout.leftMargin: __style.margin20 + + visible: leftContentVisible + + Item { + id: leftContentGroup + + width: childrenRect.width + height: childrenRect.height + + } + + MouseArea { + id: leftContentMouseAreaGroup + + anchors { + fill: parent + + leftMargin: -__style.margin20 + topMargin: -__style.margin16 + rightMargin: -__style.margin12 + bottomMargin: -__style.margin16 + } + + onClicked: function( mouse ) { + if ( root.editState === "enabled" ) { + mouse.accepted = true + root.focus = true + root.leftContentClicked() + } + } + } + } + + TextField { + id: textFieldControl + + Layout.fillWidth: true + Layout.preferredHeight: parent.height + + // Do not let TextField calculate implicitWidth automatically based on background, it causes binding loops + implicitWidth: width + + readOnly: root.editState === "readOnly" || root.editState === "disabled" + + // Ensure the text is scrolled to the beginning + Component.onCompleted: ensureVisible( 0 ) + + color: { + if ( root.editState === "readOnly" ) return __style.nightColor + if ( root.editState === "enabled" ) return __style.nightColor + if ( root.editState === "disabled" ) return __style.mediumGreyColor + return __style.nightColor + } + + topPadding: 0 + bottomPadding: 0 + leftPadding: leftContentGroupContainer.visible ? 0 : __style.margin20 + rightPadding: rightContentGroupContainer.visible ? 0 : __style.margin20 + + inputMethodHints: Qt.ImhNoPredictiveText + + placeholderTextColor: __style.darkGreyColor + + font: __style.p5 + + background: Rectangle { color: __style.transparentColor } + + onTextEdited: root.textEdited( text ) + + onReleased: { + if ( root.editState !== "readOnly" ) { + root.textClicked() + } + } + } + + Item { + id: rightContentGroupContainer + + Layout.preferredHeight: rightContentGroup.height + Layout.preferredWidth: rightContentGroup.width + Layout.rightMargin: __style.margin20 + + visible: rightContentVisible + + Item { + id: rightContentGroup + + width: childrenRect.width + height: childrenRect.height + } + + MouseArea { + id: rightContentMouseAreaGroup + + anchors { + fill: parent + + leftMargin: -__style.margin12 + topMargin: -__style.margin16 + rightMargin: -__style.margin20 + bottomMargin: -__style.margin16 + } + + onClicked: function( mouse ) { + if ( root.editState === "enabled" ) { + mouse.accepted = true + root.focus = true + root.rightContentClicked() + } + } + } + } + } + } +} diff --git a/app/qml/dialogs/MMCloseAccountDialog.qml b/app/qml/dialogs/MMCloseAccountDialog.qml index 72f5c7bae..1e6bed258 100644 --- a/app/qml/dialogs/MMCloseAccountDialog.qml +++ b/app/qml/dialogs/MMCloseAccountDialog.qml @@ -56,7 +56,7 @@ MMDrawer { id: usernameInput width: parent.width - bgColor: __style.lightGreenColor + textFieldBackground.color: __style.lightGreenColor title: qsTr("Username") placeholderText: qsTr("Enter your username") diff --git a/app/qml/form/components/MMFeaturesListPageDrawer.qml b/app/qml/form/components/MMFeaturesListPageDrawer.qml index 562c715b3..a8b93ed42 100644 --- a/app/qml/form/components/MMFeaturesListPageDrawer.qml +++ b/app/qml/form/components/MMFeaturesListPageDrawer.qml @@ -62,15 +62,13 @@ Drawer { MMComponents.MMListSpacer { height: __style.spacing20 } MMInputs.MMSearchInput { - id: searchInput + id: searchBar width: parent.width placeholderText: qsTr("Search for features...") - onSearchTextChanged: function( text ) { - root.searchTextChanged( text ) - } + onSearchTextChanged: root.searchTextChanged( searchBar.searchText ) } MMComponents.MMListSpacer { height: __style.spacing20 } @@ -79,7 +77,7 @@ Drawer { id: listView width: parent.width - height: parent.height - 2 * __style.spacing20 - searchInput.height + height: parent.height - 2 * __style.spacing20 - searchBar.height clip: true diff --git a/app/qml/components/MMPhotoAttachment.qml b/app/qml/form/components/photo/MMPhotoAttachment.qml similarity index 92% rename from app/qml/components/MMPhotoAttachment.qml rename to app/qml/form/components/photo/MMPhotoAttachment.qml index 0590737ac..58737b3cc 100644 --- a/app/qml/components/MMPhotoAttachment.qml +++ b/app/qml/form/components/photo/MMPhotoAttachment.qml @@ -9,13 +9,14 @@ import QtQuick import QtQuick.Controls -import "." + +import "../../../components" as MMComponents Rectangle { id: root color: __style.polarColor - radius: 20 * __dp + radius: __style.radius20 property bool hasCameraCapability: true @@ -42,18 +43,18 @@ Rectangle { anchors.centerIn: parent spacing: 8 * __dp - MMIcon { + MMComponents.MMIcon { anchors.horizontalCenter: parent.horizontalCenter source: __style.addImageIcon color: __style.forestColor } - Text { + MMComponents.MMText { width: parent.width - height: 24 * __dp + font: __style.p6 text: qsTr("Take a picture") - color: __style.nightColor + horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideMiddle @@ -79,18 +80,18 @@ Rectangle { anchors.centerIn: parent spacing: 8 * __dp - MMIcon { + MMComponents.MMIcon { anchors.horizontalCenter: parent.horizontalCenter source: __style.morePhotosIcon color: __style.forestColor } - Text { + MMComponents.MMText { width: parent.width - height: 24 * __dp + font: __style.p6 text: qsTr("From gallery") - color: __style.nightColor + horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideMiddle diff --git a/app/qml/components/MMPhotoPreview.qml b/app/qml/form/components/photo/MMPhotoPreview.qml similarity index 93% rename from app/qml/components/MMPhotoPreview.qml rename to app/qml/form/components/photo/MMPhotoPreview.qml index 347cbb2d1..eb99b1c03 100644 --- a/app/qml/components/MMPhotoPreview.qml +++ b/app/qml/form/components/photo/MMPhotoPreview.qml @@ -10,6 +10,8 @@ import QtQuick import QtQuick.Controls +import "../../../components" as MMComponents + Popup { id: root @@ -22,13 +24,13 @@ Popup { closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside background: Rectangle { - color: Qt.alpha(__style.nightAlphaColor, 0.9) + color: Qt.alpha(__style.darkGreyColor, 0.9) } contentItem: Item { anchors.fill: parent - MMBusyIndicator { + MMComponents.MMBusyIndicator { anchors.centerIn: parent visible: true } @@ -67,7 +69,7 @@ Popup { width: parent.width - __style.safeAreaLeft - __style.safeAreaRight height: parent.height - __style.safeAreaBottom - __style.safeAreaTop - MMRoundButton { + MMComponents.MMRoundButton { id: closeButton anchors { @@ -82,7 +84,6 @@ Popup { previewLoader.active = false } } - } } } diff --git a/app/qml/form/editors/MMFormCalendarEditor.qml b/app/qml/form/editors/MMFormCalendarEditor.qml index 5ce240644..42f45ff8b 100644 --- a/app/qml/form/editors/MMFormCalendarEditor.qml +++ b/app/qml/form/editors/MMFormCalendarEditor.qml @@ -8,12 +8,11 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents + import "../components" as MMFormComponents -import "../../inputs" /* * Calendar (datetime) editor for QGIS Attribute Form @@ -23,7 +22,7 @@ import "../../inputs" * Should be used only within feature form. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property var _field: parent.field @@ -50,9 +49,8 @@ MMBaseInput { property bool includesDate: typeFromFieldFormat.includes("Date") property bool showSeconds: true - property alias text: textField.text - title: _fieldShouldShowTitle ? _fieldTitle : "" + text: formatText( root._fieldValue ) warningMsg: _fieldWarningMessage errorMsg: _fieldErrorMessage @@ -60,37 +58,23 @@ MMBaseInput { hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState - enabled: !_fieldIsReadOnly - hasFocus: textField.activeFocus + readOnly: _fieldIsReadOnly onCheckboxCheckedChanged: { root.rememberValueBoxClicked( checkboxChecked ) } - content: MMComponents.MMText { - id: textField - - anchors.fill: parent - - text: formatText( root._fieldValue ) - color: __style.nightColor - font: __style.p5 - } - - onContentClicked: root.openCalendar() + onTextClicked: root.openCalendar() + onRightContentClicked: root.openCalendar() - rightAction: MMComponents.MMIcon { + rightContent: MMComponents.MMIcon { id: rightIcon - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 source: __style.calendarIcon - color: root.enabled ? __style.forestColor : __style.mediumGreenColor + color: root.iconColor } - onRightActionClicked: root.openCalendar() - Loader { id: dateTimeDrawerLoader diff --git a/app/qml/form/editors/MMFormComboboxBaseEditor.qml b/app/qml/form/editors/MMFormComboboxBaseEditor.qml index c60ba5472..4dec4f8f1 100644 --- a/app/qml/form/editors/MMFormComboboxBaseEditor.qml +++ b/app/qml/form/editors/MMFormComboboxBaseEditor.qml @@ -8,11 +8,9 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic import "../../components" as MMComponents -import "../../inputs" as MMInputs +import "../../components/private" as MMPrivateComponents /* * Common dropdown (combobox) for forms (value relation and value map). @@ -21,63 +19,25 @@ import "../../inputs" as MMInputs * * Disabled state can be achieved by setting `enabled: false`. * - * See MMBaseInput for more properties. + * See MMBaseSingleLineInput for more properties. */ -MMInputs.MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root - property alias placeholderText: textField.placeholderText - property alias text: textField.text - property alias textFieldComponent: textField - property alias dropdownLoader: drawerLoader - hasFocus: textField.activeFocus - - content: TextField { - id: textField - - anchors.fill: parent - anchors.verticalCenter: parent.verticalCenter - - readOnly: true - - color: root.enabled ? __style.nightColor : __style.mediumGreenColor - placeholderTextColor: __style.nightAlphaColor - - font: __style.p5 - hoverEnabled: true + textField.readOnly: true - background: Rectangle { - color: __style.transparentColor - } - - MouseArea { - anchors.fill: parent - onClicked: function( mouse ) { - mouse.accepted = true - openDrawer() - } - } - } - - rightAction: MMComponents.MMIcon { - property bool pressed: false - - anchors.verticalCenter: parent.verticalCenter + onTextClicked: openDrawer() + rightContent: MMComponents.MMIcon { size: __style.icon24 source: __style.arrowDownIcon - color: root.enabled ? __style.forestColor : __style.mediumGreenColor + color: root.iconColor } - onRightActionClicked: { - if ( !root.enabled ) - return - - openDrawer() - } + onRightContentClicked: openDrawer() Loader { id: drawerLoader diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index 0279d7cf4..d2dccddf5 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -12,184 +12,93 @@ import QtQuick.Controls import mm 1.0 as MM -import "../../components" +import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents -Item { +MMPrivateComponents.MMBaseInput { id: root - width: parent.width - height: column.height - property var _fieldAssociatedRelation: parent.fieldAssociatedRelation property var _fieldFeatureLayerPair: parent.fieldFeatureLayerPair property var _fieldActiveProject: parent.fieldActiveProject - property string _fieldTitle: parent.fieldTitle property bool _fieldShouldShowTitle: parent.fieldShouldShowTitle - - property var model - property string title: _fieldShouldShowTitle ? _fieldTitle : "" - property string warningMsg - property string errorMsg - property int maxVisiblePhotos: -1 // -1 for showing all photos - property bool showAddImage: true - - signal showAll() - signal clicked( var path ) - signal addImage() + property string _fieldTitle: parent.fieldTitle signal openLinkedFeature( var linkedFeature ) signal createLinkedFeature( var parentFeature, var relation ) - Column { - id: column + title: _fieldShouldShowTitle ? _fieldTitle : "" - padding: 20 * __dp - spacing: 10 * __dp - width: parent.width - 40 * __dp + inputContent: ListView { + id: rowView - Item { - width: parent.width - height: 15 * __dp + width: parent.width + height: __style.row120 - Text { - width: column.width - showAllButton.width - 10 * __dp + clip: true + spacing: __style.spacing12 + orientation: ListView.Horizontal - text: root.title - font: __style.p6 - elide: Text.ElideRight - color: __style.nightColor - } + model: MM.RelationFeaturesModel { + id: rmodel - Text { - id: showAllButton + relation: root._fieldAssociatedRelation + parentFeatureLayerPair: root._fieldFeatureLayerPair + homePath: root._fieldActiveProject.homePath + } - anchors.right: parent.right + delegate: MMComponents.MMPhoto { + width: rowView.height - visible: false // for now + fillMode: Image.PreserveAspectCrop - text: qsTr("Show all") - font: __style.t4 - color: __style.forestColor + photoUrl: { + let absolutePath = model.PhotoPath - MouseArea { - anchors.fill: parent - onClicked: root.showAll() + if ( absolutePath !== '' && __inputUtils.fileExists( absolutePath ) ) { + return "file://" + absolutePath } + return '' } - } - ListView { - id: rowView - - height: 120 * __dp - width: parent.width - spacing: root.maxVisiblePhotos !== 0 ? __style.spacing12 : 0 - orientation: ListView.Horizontal - -// model: { -// if(root.maxVisiblePhotos >= 0 && root.model.length > root.maxVisiblePhotos) { -// return root.model.slice(0, root.maxVisiblePhotos) -// } -// return root.model -// } - - model: MM.RelationFeaturesModel { - id: rmodel - - relation: root._fieldAssociatedRelation - parentFeatureLayerPair: root._fieldFeatureLayerPair - homePath: root._fieldActiveProject.homePath + onClicked: function( path ) { + root.openLinkedFeature( model.FeaturePair ) } + } - delegate: MMPhoto { - width: rowView.height - - fillMode: Image.PreserveAspectCrop + header: addFeatureComponent + } - photoUrl: { - let absolutePath = model.PhotoPath + Component { + id: addFeatureComponent - if ( absolutePath !== '' && __inputUtils.fileExists( absolutePath ) ) { - return "file://" + absolutePath - } - return '' - } + Row { - onClicked: function(path) { - root.clicked(path) - root.openLinkedFeature( model.FeaturePair ) - } - } + Rectangle { + height: rowView.height + width: height - header: Row { - visible: root.showAddImage - - Rectangle { - width: visible ? height : 0 - height: rowView.height - radius: 20 * __dp - border.width: 2 * __dp - border.color: root.errorMsg.length > 0 ? __style.negativeColor : root.warningMsg.length > 0 ? __style.warningColor : __style.polarColor - color: (root.errorMsg.length > 0 || root.warningMsg.length > 0) ? __style.errorBgInputColor : __style.polarColor - - MMIcon { - anchors.centerIn: parent - source: __style.addImageIcon - color: root.errorMsg.length > 0 ? __style.grapeColor : root.warningMsg.length > 0 ? __style.earthColor : __style.forestColor - size: __style.icon32 - } + radius: __style.radius20 + color: __style.polarColor - MouseArea { - anchors.fill: parent - onClicked: { - root.addImage() - root.createLinkedFeature( root._fieldFeatureLayerPair, root._fieldAssociatedRelation ) - } - } + MMComponents.MMIcon { + anchors.centerIn: parent + source: __style.addImageIcon + color: root.errorMsg.length > 0 ? __style.grapeColor : root.warningMsg.length > 0 ? __style.earthColor : __style.forestColor + size: __style.icon32 } - Item { - width: visible ? rowView.spacing : 0 - height: rowView.height + MouseArea { + anchors.fill: parent + onClicked: { + root.createLinkedFeature( root._fieldFeatureLayerPair, root._fieldAssociatedRelation ) + } } } - footer: MMMorePhoto { - width: visible ? rowView.height + rowView.spacing: 0 - -// hiddenPhotoCount: root.model.length - root.maxVisiblePhotos -// visible: root.maxVisiblePhotos >= 0 && root.model.length > root.maxVisiblePhotos -// photoUrl: visible ? model[root.maxVisiblePhotos] : "" -// space: visible ? rowView.spacing : 0 - - visible: false - - onClicked: root.showAll() - } - } - - Row { - id: msgRow - - spacing: 4 * __dp - - MMIcon { - id: msgIcon - - source: visible ? __style.errorCircleIcon : "" - color: root.errorMsg.length > 0 ? __style.negativeColor : __style.warningColor - size: __style.icon16 - visible: root.errorMsg.length > 0 || root.warningMsg.length > 0 - } - - Text { - width: column.width - msgRow.spacing - msgIcon.size - - text: root.errorMsg.length > 0 ? root.errorMsg : root.warningMsg - font: __style.t4 - wrapMode: Text.WordWrap - visible: root.errorMsg.length > 0 || root.warningMsg.length > 0 + MMComponents.MMListSpacer { + width: rowView.spacing } } } diff --git a/app/qml/form/editors/MMFormNumberEditor.qml b/app/qml/form/editors/MMFormNumberEditor.qml index 76952953f..3ae2a070d 100644 --- a/app/qml/form/editors/MMFormNumberEditor.qml +++ b/app/qml/form/editors/MMFormNumberEditor.qml @@ -8,11 +8,9 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../../components" -import "../../inputs" +import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents /* * Number (range editable) editor for QGIS Attribute Form @@ -22,7 +20,7 @@ import "../../inputs" * Should be used only within feature form. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property var _fieldValue: parent.fieldValue @@ -39,20 +37,15 @@ MMBaseInput { property bool _fieldRememberValueSupported: parent.fieldRememberValueSupported property bool _fieldRememberValueState: parent.fieldRememberValueState - property alias placeholderText: numberInput.placeholderText - signal editorValueChanged( var newValue, var isNull ) signal rememberValueBoxClicked( bool state ) - title: _fieldShouldShowTitle ? _fieldTitle : "" + readOnly: _fieldIsReadOnly errorMsg: _fieldErrorMessage warningMsg: _fieldWarningMessage - enabled: !_fieldIsReadOnly - hasFocus: numberInput.activeFocus - hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState @@ -60,93 +53,95 @@ MMBaseInput { root.rememberValueBoxClicked( checkboxChecked ) } - leftAction: MMIcon { + leftContent: MMComponents.MMIcon { id: leftIcon - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 source: __style.minusIcon - color: enabled ? __style.forestColor : __style.mediumGreenColor - enabled: Number( numberInput.text ) - internal.step >= internal.from - } - - onLeftActionClicked: { - if ( leftIcon.enabled ) - { - let decremented = Number( numberInput.text ) - internal.step - root.editorValueChanged( decremented.toFixed( internal.precision ), false ) + color: { + if ( root.editState !== "enabled" ) return __style.mediumGreyColor + if ( internal.canSubtractStep ) { + if ( root.validationState === "error" ) return __style.grapeColor + if ( root.validationState === "warning" ) return __style.earthColor + return __style.forestColor + } + else { + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + return __style.mediumGreyColor + } } } - content: Item { - anchors.fill: parent - - Row { - height: parent.height - anchors.horizontalCenter: parent.horizontalCenter - clip: true + textField { + text: root._fieldValue === undefined || root._fieldValueIsNull ? '' : root._fieldValue - TextField { - id: numberInput + clip: true - height: parent.height + // AlignHCenter with optional suffix + leftPadding: Math.max( 0, ( textField.width / 2 - textField.contentWidth / 2 ) - ( internal.suffix ? suffixText.width / 2 : 0 ) ) - text: root._fieldValue === undefined || root._fieldValueIsNull ? '' : root._fieldValue + inputMethodHints: Qt.ImhFormattedNumbersOnly - placeholderTextColor: __style.nightAlphaColor - color: __style.nightColor + background: Rectangle { + color: "transparent" - font: __style.p5 + // Suffix is added as a part of the background property in order to not block clicks to the textField + MMComponents.MMText { + id: suffixText - clip: true - hoverEnabled: true + property real maxWidth: textField.width / 2 - verticalAlignment: Qt.AlignVCenter - inputMethodHints: Qt.ImhFormattedNumbersOnly + width: Math.min( implicitWidth + __style.margin4, maxWidth ) + x: textField.leftPadding + textField.contentWidth + __style.margin4 + anchors.verticalCenter: parent.verticalCenter - onTextEdited: { - let val = text.replace( ",", "." ).replace( / /g, '' ) // replace comma with dot + color: __style.nightColor + font: __style.p5 - root.editorValueChanged( val, val === "" ) - } + text: internal.suffix - background: Rectangle { - color: __style.transparentColor - } + visible: internal.suffix && textField.text.length > 0 } + } + } - Text { - id: suffix - - text: internal.suffix ? ' ' + internal.suffix : "" // to make sure there is a space between the number and the suffix - - visible: internal.suffix !== "" && numberInput.text !== "" - - height: parent.height - verticalAlignment: Qt.AlignVCenter + rightContent: MMComponents.MMIcon { + id: rightIcon - font: __style.p5 - color: numberInput.color + size: __style.icon24 + source: __style.plusIcon + color: { + if ( root.editState !== "enabled" ) return __style.mediumGreyColor + if ( internal.canAddStep ) { + if ( root.validationState === "error" ) return __style.grapeColor + if ( root.validationState === "warning" ) return __style.earthColor + return __style.forestColor + } + else { + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + return __style.mediumGreyColor } } } - rightAction: MMIcon { - id: rightIcon + onLeftContentClicked: { + if ( internal.canSubtractStep ) { + let decremented = Number( textField.text ) - internal.step + root.editorValueChanged( decremented.toFixed( internal.precision ), false ) + } + } - anchors.verticalCenter: parent.verticalCenter + onTextEdited: ( text ) => { + let val = text.replace( ",", "." ).replace( / /g, '' ) // replace comma with dot - size: __style.icon24 - source: __style.plusIcon - color: enabled ? __style.forestColor : __style.mediumGreenColor - enabled: Number( numberInput.text ) + internal.step <= internal.to + root.editorValueChanged( val, val === "" ) } - onRightActionClicked: { - if ( rightIcon.enabled ) - { - let incremented = Number( numberInput.text ) + internal.step + onRightContentClicked: { + if ( internal.canAddStep ) { + let incremented = Number( textField.text ) + internal.step root.editorValueChanged( incremented.toFixed( internal.precision ), false ) } } @@ -164,6 +159,9 @@ MMBaseInput { // i.e. if showing 2 decimals, smallest increment will be 0.01 // https://github.com/qgis/QGIS/blob/a038a79997fb560e797daf3903d94c7d68e25f42/src/gui/editorwidgets/qgsdoublespinbox.cpp#L83-L87 property real step: Math.max(_fieldConfig["Step"], Math.pow( 10.0, 0.0 - precision )) + + property bool canSubtractStep: Number( root.textField.text ) - internal.step >= internal.from + property bool canAddStep: Number( root.textField.text ) + internal.step <= internal.to } // on press and hold behavior can be used from here: diff --git a/app/qml/form/editors/MMFormPhotoEditor.qml b/app/qml/form/editors/MMFormPhotoEditor.qml index fa57abfa1..8c04d1138 100644 --- a/app/qml/form/editors/MMFormPhotoEditor.qml +++ b/app/qml/form/editors/MMFormPhotoEditor.qml @@ -80,7 +80,7 @@ MMFormPhotoViewer { warningMsg: _fieldWarningMessage errorMsg: _fieldErrorMessage - allowEditing: !_fieldIsReadOnly + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState @@ -204,22 +204,22 @@ MMFormPhotoViewer { let absolutePath = __inputUtils.getAbsolutePath( root._fieldValue, internal.prefixToRelativePath ) if ( root.photoComponent.status === Image.Error ) { - root.state = "notAvailable" + root.photoState = "notAvailable" absoluteImagePath = "" return } else if ( root._fieldValue && __inputUtils.fileExists( absolutePath ) ) { - root.state = "valid" + root.photoState = "valid" absoluteImagePath = "file://" + absolutePath return } else if ( !root._fieldValue || root._fieldValueIsNullfield ) { - root.state = "notSet" + root.photoState = "notSet" absoluteImagePath = "" return } - root.state = "notAvailable" + root.photoState = "notAvailable" absoluteImagePath = "file://" + absolutePath } diff --git a/app/qml/form/editors/MMFormPhotoViewer.qml b/app/qml/form/editors/MMFormPhotoViewer.qml index d9be3f054..d329cd5c7 100644 --- a/app/qml/form/editors/MMFormPhotoViewer.qml +++ b/app/qml/form/editors/MMFormPhotoViewer.qml @@ -8,9 +8,10 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import "../../components" -import "../../inputs" + +import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents +import "../components/photo" as MMPhotoComponents /* * Photo viewer for feature form. @@ -20,83 +21,90 @@ import "../../inputs" * Serves as a base class for MMPhotoFormEditor. */ -MMBaseInput { +MMPrivateComponents.MMBaseInput { id: root property url photoUrl: "" property bool hasCameraCapability: true - // Do not use "enabled" since we want to always be able to open image previews - property bool allowEditing: true - - property alias photoComponent: photo + property var photoComponent: photo + property alias photoState: photoStateGroup.state signal trashClicked() signal capturePhotoClicked() signal chooseFromGalleryClicked() - contentItemHeight: 160 * __dp - spacing: 0 - radius: 20 * __dp - - states: [ - State { - name: "valid" - }, - State { - name: "notSet" - }, - State { - name: "notAvailable" - } - ] - - state: "notSet" + StateGroup { + id: photoStateGroup + + states: [ + State { + name: "valid" + }, + State { + name: "notSet" + }, + State { + name: "notAvailable" + } + ] - onContentClicked: { - if ( photo.status === Image.Ready ) { - previewLoader.active = true - previewLoader.focus = true - } + state: "notSet" } - content: Item { - MMPhoto { + + inputContent: Rectangle { + width: parent.width + height: __style.row160 + + color: __style.polarColor + radius: __style.radius20 + + MMComponents.MMPhoto { id: photo - width: root.width - height: root.contentItemHeight - visible: root.state !== "notSet" - photoUrl: root.photoUrl + width: parent.width + height: parent.height + visible: photoStateGroup.state !== "notSet" + + photoUrl: root.photoUrl fillMode: Image.PreserveAspectCrop MouseArea { anchors.fill: parent - onClicked: root.contentClicked() + onClicked: { + if ( photo.status === Image.Ready ) { + previewLoader.active = true + previewLoader.focus = true + } + } } - MMRoundButton { + MMComponents.MMRoundButton { anchors { right: parent.right bottom: parent.bottom - rightMargin: 10 * __dp - bottomMargin: 10 * __dp + rightMargin: __style.margin10 + bottomMargin: __style.margin10 } bgndColor: __style.negativeColor iconSource: __style.deleteIcon iconColor: __style.grapeColor - visible: root.allowEditing && root.state !== "notSet" + + visible: root.editState === "enabled" && photoStateGroup.state !== "notSet" + onClicked: root.trashClicked() } } - MMPhotoAttachment { - width: root.width - height: root.contentItemHeight - visible: root.state === "notSet" - enabled: root.allowEditing + MMPhotoComponents.MMPhotoAttachment { + width: parent.width + height: parent.height + + visible: photoStateGroup.state === "notSet" + enabled: root.editState === "enabled" hasCameraCapability: root.hasCameraCapability @@ -116,7 +124,7 @@ MMBaseInput { Component { id: previewComponent - MMPhotoPreview { + MMPhotoComponents.MMPhotoPreview { photoUrl: root.photoUrl } } diff --git a/app/qml/form/editors/MMFormRelationEditor.qml b/app/qml/form/editors/MMFormRelationEditor.qml index 93a89c791..0931e2415 100644 --- a/app/qml/form/editors/MMFormRelationEditor.qml +++ b/app/qml/form/editors/MMFormRelationEditor.qml @@ -9,12 +9,11 @@ import QtQuick import QtQuick.Controls -import QtQuick.Controls.Basic import mm 1.0 as MM -import "../../inputs" as MMInputs import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents import "../components" as MMFormComponents /* @@ -25,7 +24,7 @@ import "../components" as MMFormComponents * Should be used only within feature form. */ -MMInputs.MMBaseInput { +MMPrivateComponents.MMBaseInput { id: root property var _fieldAssociatedRelation: parent.fieldAssociatedRelation @@ -38,24 +37,39 @@ MMInputs.MMBaseInput { signal openLinkedFeature( var linkedFeature ) signal createLinkedFeature( var parentFeature, var relation ) - contentItemHeight: privates.itemHeight * privates.rows + 2 * flow.spacing + 20 * __dp - Component.onCompleted: root.recalculateVisibleItems() onWidthChanged: root.recalculateVisibleItems() title: _fieldShouldShowTitle ? _fieldTitle : "" - content: Rectangle { - width: root.width - 2 * root.spacing - height: root.contentItemHeight + inputContent: Rectangle { + width: parent.width + height: privates.itemHeight * privates.rows + 2 * flow.spacing + 2 * __style.margin12 + + radius: __style.radius12 color: __style.polarColor + MouseArea { + anchors.fill: parent + onClicked: function( mouse ) { + mouse.accepted = true + listLoader.active = true + listLoader.focus = true + } + } + Flow { id: flow - anchors.fill: parent - anchors.margins: 10 * __dp - spacing: 8 * __dp + anchors { + fill: parent + topMargin: __style.margin12 + bottomMargin: __style.margin12 + leftMargin: __style.margin20 + rightMargin: __style.margin20 + } + + spacing: __style.margin8 clip: true Rectangle { @@ -150,8 +164,6 @@ MMInputs.MMBaseInput { MouseArea { anchors.fill: parent onClicked: { - if ( !root.enabled ) - return listLoader.active = true listLoader.focus = true } diff --git a/app/qml/form/editors/MMFormRelationReferenceEditor.qml b/app/qml/form/editors/MMFormRelationReferenceEditor.qml index 880ae536a..e681c987d 100644 --- a/app/qml/form/editors/MMFormRelationReferenceEditor.qml +++ b/app/qml/form/editors/MMFormRelationReferenceEditor.qml @@ -8,30 +8,36 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls import mm 1.0 as MM -import "../../inputs" as MMInputs import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents import "../components" as MMFormComponents -MMInputs.MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property var _fieldValue: parent.fieldValue property var _fieldConfig: parent.fieldConfig property var _fieldActiveProject: parent.fieldActiveProject - property bool _fieldValueIsNull: parent.fieldValueIsNull + property bool _fieldIsReadOnly: parent.fieldIsReadOnly - property string _fieldTitle: parent.fieldTitle + property bool _fieldShouldShowTitle: parent.fieldShouldShowTitle + property string _fieldTitle: parent.fieldTitle + property string _fieldErrorMessage: parent.fieldErrorMessage + property string _fieldWarningMessage: parent.fieldWarningMessage + + property bool _fieldRememberValueSupported: parent.fieldRememberValueSupported + property bool _fieldRememberValueState: parent.fieldRememberValueState signal openLinkedFeature( /* FeaturePair */ var linkedFeature ) signal editorValueChanged( var newValue, bool isNull ) + signal rememberValueBoxClicked( bool state ) on_FieldValueChanged: { - title.text = rModel.attributeFromForeignKey( root._fieldValue, MM.FeaturesModel.FeatureTitle ) || "" + textField.text = rModel.attributeFromForeignKey( root._fieldValue, MM.FeaturesModel.FeatureTitle ) || "" } title: _fieldShouldShowTitle ? _fieldTitle : "" @@ -39,7 +45,7 @@ MMInputs.MMBaseInput { errorMsg: _fieldErrorMessage warningMsg: _fieldWarningMessage - enabled: !_fieldIsReadOnly + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState @@ -48,51 +54,35 @@ MMInputs.MMBaseInput { root.rememberValueBoxClicked( checkboxChecked ) } - MM.RelationReferenceFeaturesModel { - id: rModel - - config: root._fieldConfig - project: root._fieldActiveProject - - onModelReset: title.text = rModel.attributeFromForeignKey( root._fieldValue, MM.FeaturesModel.FeatureTitle ) || "" - } - - content: Text { - id: title + textField.readOnly: true - anchors.fill: parent - font: __style.p5 - text: root._fieldValue - color: __style.nightColor - verticalAlignment: Text.AlignVCenter - } - - onContentClicked: { + textField.onReleased: { // can be opened even when the field is readonly let featurePair = rModel.attributeFromForeignKey( root._fieldValue, MM.FeaturesModel.FeaturePair ) - - if ( featurePair == null || !featurePair.valid ) return + if ( featurePair === null || !featurePair.valid ) return openLinkedFeature( featurePair ) } - rightAction: MMComponents.MMIcon { - id: rightIcon - - anchors.verticalCenter: parent.verticalCenter - + rightContent: MMComponents.MMIcon { size: __style.icon24 source: __style.linkIcon - color: enabled ? __style.forestColor : __style.mediumGreenColor + color: root.iconColor } - onRightActionClicked: { - if ( root._fieldIsReadOnly ) - return - + onRightContentClicked: { listLoader.active = true listLoader.focus = true } + MM.RelationReferenceFeaturesModel { + id: rModel + + config: root._fieldConfig + project: root._fieldActiveProject + + onModelReset: textField.text = rModel.attributeFromForeignKey( root._fieldValue, MM.FeaturesModel.FeatureTitle ) || "" + } + Loader { id: listLoader diff --git a/app/qml/form/editors/MMFormRichTextViewer.qml b/app/qml/form/editors/MMFormRichTextViewer.qml index 30479e105..ec226d191 100644 --- a/app/qml/form/editors/MMFormRichTextViewer.qml +++ b/app/qml/form/editors/MMFormRichTextViewer.qml @@ -8,48 +8,48 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import "../components" -Item { +import "../../components/private" as MMPrivateComponents + +MMPrivateComponents.MMBaseInput { id: root property var _fieldValue: parent.fieldValue property var _fieldConfig: parent.fieldConfig - property real padding: 11 * __dp + property bool _fieldShouldShowTitle: parent.fieldShouldShowTitle + + property string _fieldTitle: parent.fieldTitle - height: textArea.height - width: parent.width + title: _fieldShouldShowTitle ? _fieldTitle : "" - Rectangle { // background - width: root.width - height: root.height - border.width: 2 * __dp - border.color: __style.transparentColor + inputContent: Rectangle { // background + width: parent.width + height: textArea.implicitHeight color: __style.polarColor radius: __style.radius12 - } - Text { - id: textArea + Text { // intentionally not MMText to that bottom padding works correctly in rich text mode + id: textArea + + width: parent.width - wrapMode: Text.Wrap - font: __style.p5 - color: __style.nightColor + wrapMode: Text.Wrap + font: __style.p5 + color: __style.nightColor - text: root._fieldValue !== undefined ? root._fieldValue : '' - textFormat: _fieldConfig['UseHtml'] ? TextEdit.RichText : TextEdit.PlainText + text: root._fieldValue !== undefined ? root._fieldValue : '' + textFormat: root._fieldConfig['UseHtml'] ? TextEdit.RichText : TextEdit.PlainText - width: root.width - topPadding: root.padding - bottomPadding: root.padding - leftPadding: root.padding - rightPadding: root.padding + topPadding: __style.margin12 + bottomPadding: __style.margin12 + leftPadding: __style.margin20 + rightPadding: __style.margin20 - onLinkActivated: function( link ) { - Qt.openUrlExternally( link ) + onLinkActivated: function( link ) { + Qt.openUrlExternally( link ) + } } } } diff --git a/app/qml/form/editors/MMFormScannerEditor.qml b/app/qml/form/editors/MMFormScannerEditor.qml index a926742f4..f2defc302 100644 --- a/app/qml/form/editors/MMFormScannerEditor.qml +++ b/app/qml/form/editors/MMFormScannerEditor.qml @@ -8,10 +8,9 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../../components" -import "../../inputs" + +import "../../components" as MMComponents +import "../../components/private" as MMPrivateComponents /* * QR/Barcode scanner editor for QGIS Attribute Form @@ -21,7 +20,7 @@ import "../../inputs" * Should be used only within feature form. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property var _fieldValue: parent.fieldValue @@ -38,9 +37,6 @@ MMBaseInput { property bool _fieldRememberValueSupported: parent.fieldRememberValueSupported property bool _fieldRememberValueState: parent.fieldRememberValueState - property alias placeholderText: textField.placeholderText - property alias text: textField.text - signal editorValueChanged( var newValue, bool isNull ) signal rememberValueBoxClicked( bool state ) @@ -49,8 +45,7 @@ MMBaseInput { warningMsg: _fieldWarningMessage errorMsg: _fieldErrorMessage - hasFocus: textField.activeFocus - enabled: !_fieldIsReadOnly + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState @@ -59,39 +54,20 @@ MMBaseInput { root.rememberValueBoxClicked( checkboxChecked ) } - content: TextField { - id: textField - - anchors.fill: parent - anchors.verticalCenter: parent.verticalCenter - - readOnly: !root.enabled - - text: root._fieldValue === undefined || root._fieldValueIsNull ? '' : root._fieldValue + text: root._fieldValue === undefined || root._fieldValueIsNull ? '' : root._fieldValue - color: __style.nightColor - placeholderTextColor: __style.nightAlphaColor + onTextEdited: root.editorValueChanged( root.text, root.text === "" ) - font: __style.p5 - hoverEnabled: true - - background: Rectangle { color: __style.transparentColor } - - onTextEdited: root.editorValueChanged( textField.text, textField.text === "" ) - } - - rightAction: MMIcon { + rightContent: MMComponents.MMIcon { property bool pressed: false - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 source: __style.qrCodeIcon - color: root.enabled ? __style.forestColor : __style.mediumGreenColor + color: root.iconColor } - onRightActionClicked: { - if ( !root.enabled ) + onRightContentClicked: { + if ( root.editState !== "enabled" ) return if (!__inputUtils.acquireCameraPermission()) @@ -112,7 +88,7 @@ MMBaseInput { Component { id: readerComponent - MMCodeScanner { + MMComponents.MMCodeScanner { focus: true onClosed: codeScannerLoader.active = false diff --git a/app/qml/form/editors/MMFormSliderEditor.qml b/app/qml/form/editors/MMFormSliderEditor.qml index 573ea80dc..6465ecc62 100644 --- a/app/qml/form/editors/MMFormSliderEditor.qml +++ b/app/qml/form/editors/MMFormSliderEditor.qml @@ -10,9 +10,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic -import QtQuick.Layouts -import "../../inputs" +import "../../components/private" as MMPrivateComponents /* * Number slider editor for QGIS Attribute Form @@ -22,7 +21,7 @@ import "../../inputs" * Should be used only within feature form. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property var _fieldValue: parent.fieldValue @@ -46,8 +45,7 @@ MMBaseInput { warningMsg: _fieldWarningMessage errorMsg: _fieldErrorMessage - hasFocus: slider.activeFocus - enabled: !_fieldIsReadOnly + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState @@ -56,67 +54,61 @@ MMBaseInput { root.rememberValueBoxClicked( checkboxChecked ) } - content: Item { - id: input - - anchors.fill: parent + textField { + text: Number( slider.value ).toFixed( internal.precision ).toLocaleString( root.locale ) + ' ' + internal.suffix + readOnly: true + } - RowLayout { - id: rowLayout + rightContentMouseArea.enabled: false - anchors.fill: parent + rightContent: Slider { + id: slider - Text { - id: valueLabel + width: root.width / 2 - Layout.preferredWidth: rowLayout.width / 2 - root.spacing - Layout.maximumWidth: rowLayout.width / 2 - root.spacing - Layout.preferredHeight: input.height - Layout.maximumHeight: input.height + to: internal.to + from: internal.from + stepSize: internal.step - elide: Text.ElideRight - text: Number( slider.value ).toFixed( internal.precision ).toLocaleString( root.locale ) + ' ' + internal.suffix + enabled: root.editState === "enabled" + value: root._fieldValue ? root._fieldValue : 0 - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font: __style.p5 - color: __style.nightColor - } + onPressedChanged: textField.focus = true + onValueChanged: root.editorValueChanged( slider.value, false ) - Slider { - id: slider + background: Rectangle { + x: slider.leftPadding + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + width: slider.availableWidth + height: __style.row4 + radius: __style.radius2 - Layout.fillWidth: true - Layout.maximumHeight: input.height - Layout.preferredHeight: input.height + color: { + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + return __style.lightGreenColor + } - to: internal.to - from: internal.from - stepSize: internal.step - value: root._fieldValue ? root._fieldValue : 0 + Rectangle { + // fill indicator + height: parent.height + width: slider.visualPosition * parent.width - onValueChanged: root.editorValueChanged( slider.value, false ) + color: root.iconColor - background: Rectangle { - x: slider.leftPadding - y: slider.topPadding + slider.availableHeight / 2 - height / 2 - width: slider.availableWidth - height: 4 * __dp - radius: 2 * __dp + radius: __style.radius2 + } + } - color: __style.lightGreenColor - } + handle: Rectangle { + x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + width: 20 * __dp + height: width - handle: Rectangle { - x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) - y: slider.topPadding + slider.availableHeight / 2 - height / 2 - width: 20 * __dp - height: width - radius: height / 2 + radius: height / 2 - color: root.enabled ? __style.forestColor : __style.lightGreenColor - } - } + color: root.iconColor } } diff --git a/app/qml/form/editors/MMFormSwitchEditor.qml b/app/qml/form/editors/MMFormSwitchEditor.qml index 365f64409..7c7ff20cc 100644 --- a/app/qml/form/editors/MMFormSwitchEditor.qml +++ b/app/qml/form/editors/MMFormSwitchEditor.qml @@ -8,8 +8,7 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import "../../components" + import "../../inputs" /* @@ -18,9 +17,10 @@ import "../../inputs" * These properties are injected here via 'fieldXYZ' properties and captured with underscore `_`. * * Should be used only within feature form. + * See MMBaseSingleLineInput */ -MMBaseInput { +MMSwitchInput { id: root property var _field: parent.field @@ -45,47 +45,22 @@ MMBaseInput { warningMsg: _fieldWarningMessage errorMsg: _fieldErrorMessage - enabled: !_fieldIsReadOnly - - hasFocus: rightSwitch.focus + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState - onCheckboxCheckedChanged: { - root.rememberValueBoxClicked( checkboxChecked ) - } - - content: Text { - id: textField - - width: parent.width - anchors.verticalCenter: parent.verticalCenter + text: checked ? internal.checkedStateValue : internal.uncheckedStateValue - text: rightSwitch.checked ? internal.checkedStateValue : internal.uncheckedStateValue + checked: _fieldValue === internal.checkedStateValue - color: __style.nightColor - font: __style.p5 - elide: Text.ElideRight + onToggled: { + let newVal = checked ? internal.checkedStateValue : internal.uncheckedStateValue + editorValueChanged( newVal, false ) } - onContentClicked: { - rightSwitch.toggle() - } - - rightAction: MMSwitch { - id: rightSwitch - - height: parent.height - x: -__style.margin20 - - uncheckedBgColor: __style.lightGreenColor - checked: root._fieldValue === internal.checkedStateValue - - onCheckedChanged: { - let newVal = rightSwitch.checked ? internal.checkedStateValue : internal.uncheckedStateValue - root.editorValueChanged( newVal, false ) - } + onCheckboxCheckedChanged: { + rememberValueBoxClicked( checkboxChecked ) } function getConfigValue( configValue, defaultValue ) { diff --git a/app/qml/form/editors/MMFormTextEditor.qml b/app/qml/form/editors/MMFormTextEditor.qml index 68122a5e6..49eeb53a8 100644 --- a/app/qml/form/editors/MMFormTextEditor.qml +++ b/app/qml/form/editors/MMFormTextEditor.qml @@ -10,7 +10,7 @@ import QtQuick import QtQuick.Controls -import "../../inputs" +import "../../components/private" as MMPrivateComponents /* * Text Edit for QGIS Attribute Form @@ -18,10 +18,10 @@ import "../../inputs" * These properties are injected here via 'fieldXYZ' properties and captured with underscore `_`. * * Should be used only within feature form. - * See MMTextInput + * See MMBaseSingleLineInput */ -MMTextInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property var _field: parent.field @@ -43,12 +43,8 @@ MMTextInput { text: _fieldValue === undefined || _fieldValueIsNull ? '' : _fieldValue - showClearIcon: false - readOnly: _fieldIsReadOnly - textFieldComponent.readOnly: _fieldIsReadOnly - textFieldComponent.inputMethodHints: root._field.isNumeric ? Qt.ImhFormattedNumbersOnly : Qt.ImhNone - textFieldComponent.color: __style.nightColor + textField.inputMethodHints: root._field.isNumeric ? Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly : Qt.ImhNoPredictiveText title: _fieldShouldShowTitle ? _fieldTitle : "" @@ -58,7 +54,7 @@ MMTextInput { hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState - textFieldComponent.maximumLength: { + textField.maximumLength: { if ( ( !root._field.isNumeric ) && ( root._field.length > 0 ) ) { return root._field.length } @@ -79,10 +75,6 @@ MMTextInput { root.editorValueChanged( val, val === "" ) } - // Avoid Android's uncommited text - // Could in theory be fixed with `inputMethodComposing` TextInput property instead - textFieldComponent.onPreeditTextChanged: if ( __androidUtils.isAndroid ) Qt.inputMethod.commit() - QtObject { id: internal diff --git a/app/qml/form/editors/MMFormTextMultilineEditor.qml b/app/qml/form/editors/MMFormTextMultilineEditor.qml index 2627573d1..fd82f401a 100644 --- a/app/qml/form/editors/MMFormTextMultilineEditor.qml +++ b/app/qml/form/editors/MMFormTextMultilineEditor.qml @@ -9,9 +9,12 @@ import QtQuick import QtQuick.Controls + +// To ignore the warning "The current style does not support customization" +// see from https://stackoverflow.com/questions/76625756/the-current-style-does-not-support-customization-of-this-control import QtQuick.Controls.Basic -import "../../components" -import "../../inputs" + +import "../../components/private" as MMPrivateComponents /* * Text multiline editor for QGIS Attribute Form @@ -20,7 +23,7 @@ import "../../inputs" * * Should be used only within feature form. */ -MMBaseInput { +MMPrivateComponents.MMBaseInput { id: root property var _fieldValue: parent.fieldValue @@ -37,11 +40,6 @@ MMBaseInput { property bool _fieldRememberValueSupported: parent.fieldRememberValueSupported property bool _fieldRememberValueState: parent.fieldRememberValueState - property alias placeholderText: textArea.placeholderText - property string text: _fieldValue === undefined || _fieldValueIsNull ? '' : _fieldValue - - property int minimumRows: 3 - signal editorValueChanged( var newValue, var isNull ) signal rememberValueBoxClicked( bool state ) @@ -50,7 +48,7 @@ MMBaseInput { warningMsg: _fieldWarningMessage errorMsg: _fieldErrorMessage - hasFocus: textArea.activeFocus + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState @@ -59,46 +57,79 @@ MMBaseInput { root.rememberValueBoxClicked( checkboxChecked ) } - contentItemHeight: { - const minHeight = 34 * __dp + metrics.height * root.minimumRows - var realHeight = textArea.y + textArea.contentHeight + 2 * textArea.verticalPadding - return realHeight < minHeight ? minHeight : realHeight - } - - content: TextArea { + inputContent: TextArea { id: textArea - property real verticalPadding: 11 * __dp - - y: textArea.verticalPadding - height: contentHeight + textArea.verticalPadding width: parent.width + height: Math.max( implicitHeight, internal.minHeight ) - readOnly: root._fieldIsReadOnly - - text: root.text + text: _fieldValue === undefined || _fieldValueIsNull ? '' : _fieldValue textFormat: root._fieldConfig['UseHtml'] ? TextEdit.RichText : TextEdit.PlainText - hoverEnabled: true - placeholderTextColor: __style.nightAlphaColor - color: __style.nightColor + topPadding: __style.margin12 + bottomPadding: __style.margin12 + leftPadding: __style.margin20 + rightPadding: __style.margin20 - font: __style.p5 - wrapMode: Text.WordWrap + wrapMode: TextEdit.Wrap - onLinkActivated: function( link ) { - Qt.openUrlExternally( link ) + font: __style.p5 + color: { + if ( root.editState === "readOnly" ) return __style.nightColor + if ( root.editState === "enabled" ) return __style.nightColor + if ( root.editState === "disabled" ) return __style.mediumGreyColor + return __style.nightColor } + placeholderTextColor: __style.darkGreyColor + + inputMethodHints: Qt.ImhNoPredictiveText - onTextChanged: root.editorValueChanged( text, text === "" ) + readOnly: root.editState !== "enabled" - // Avoid Android's uncommited text - // Could in theory be fixed with `inputMethodComposing` TextInput property instead - onPreeditTextChanged: if ( __androidUtils.isAndroid ) Qt.inputMethod.commit() + background: Rectangle { + + color: { + if ( root.editState !== "enabled" ) return __style.polarColor + if ( root.validationState === "error" ) return __style.negativeUltraLightColor + if ( root.validationState === "warning" ) return __style.negativeUltraLightColor + + return __style.polarColor + } + + border.width: { + if ( root.validationState === "error" ) return __style.width2 + if ( root.validationState === "warning" ) return __style.width2 + if ( textArea.activeFocus ) return __style.width2 + if ( textArea.hovered ) return __style.width1 + return 0 + } + + border.color: { + if ( root.editState !== "enabled" ) return __style.polarColor + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + if ( textArea.activeFocus ) return __style.forestColor + if ( textArea.hovered ) return __style.forestColor + + return __style.polarColor + } + + radius: __style.radius12 + } + + onLinkActivated: ( link ) => Qt.openUrlExternally( link ) + onTextChanged: root.editorValueChanged( textArea.text, textArea.text === "" ) } FontMetrics { id: metrics font: textArea.font } + + QtObject { + id: internal + + // Minimum height for multiline is 3 lines + paddings + property real minHeight: metrics.height * 3 + textArea.topPadding + textArea.bottomPadding + } } diff --git a/app/qml/form/editors/MMFormValueMapEditor.qml b/app/qml/form/editors/MMFormValueMapEditor.qml index f4eeca432..45c4c9f3d 100644 --- a/app/qml/form/editors/MMFormValueMapEditor.qml +++ b/app/qml/form/editors/MMFormValueMapEditor.qml @@ -47,13 +47,11 @@ MMFormComboboxBaseEditor { errorMsg: _fieldErrorMessage warningMsg: _fieldWarningMessage - enabled: !_fieldIsReadOnly + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState - textFieldComponent.color: __style.nightColor - onCheckboxCheckedChanged: { root.rememberValueBoxClicked( checkboxChecked ) } diff --git a/app/qml/form/editors/MMFormValueRelationEditor.qml b/app/qml/form/editors/MMFormValueRelationEditor.qml index aaad6c310..d43c5a950 100644 --- a/app/qml/form/editors/MMFormValueRelationEditor.qml +++ b/app/qml/form/editors/MMFormValueRelationEditor.qml @@ -48,13 +48,11 @@ MMFormComboboxBaseEditor { errorMsg: _fieldErrorMessage warningMsg: _fieldWarningMessage - enabled: !_fieldIsReadOnly + readOnly: _fieldIsReadOnly hasCheckbox: _fieldRememberValueSupported checkboxChecked: _fieldRememberValueState - textFieldComponent.color: __style.nightColor - on_FieldValueChanged: { vrModel.pair = root._fieldFeatureLayerPair } diff --git a/app/qml/inputs/MMBaseInput.qml b/app/qml/inputs/MMBaseInput.qml deleted file mode 100644 index 7c117a3ec..000000000 --- a/app/qml/inputs/MMBaseInput.qml +++ /dev/null @@ -1,198 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -import QtQuick -import QtQuick.Controls -import QtQml.Models -import QtQuick.Layouts - -import "../components" -import "." - -//! This is a base class for all inputs/form editors, do not use this in the app directly - -Item { - id: root - - signal contentClicked() - signal leftActionClicked() - signal rightActionClicked() - - property alias title: titleItem.text - property alias leftAction: leftActionContainer.children - property alias content: contentContainer.children - property alias rightAction: rightActionContainer.children - property string warningMsg - property string errorMsg - property bool hasFocus: false - property color bgColor: __style.polarColor - property bool hasCheckbox: false - property alias checkboxChecked: checkbox.checked - - property real contentItemHeight: 50 * __dp - - property real spacing: 15 * __dp - property real radius: __style.radius12 - - width: parent.width - height: mainColumn.height - - Column { - id: mainColumn - - spacing: 6 * __dp - anchors.left: parent.left - anchors.right: parent.right - - Row { - id: titleRow - - spacing: 4 * __dp - width: parent.width - visible: titleItem.text.length > 0 - - MMCheckBox { - id: checkbox - - small: true - visible: root.hasCheckbox - } - Text { - id: titleItem - - width: parent.width - checkbox.width - titleRow.spacing - - font: __style.p6 - wrapMode: Text.WordWrap - } - } - - Item { - height: root.contentItemHeight - anchors.left: parent.left - anchors.right: parent.right - - Rectangle { - id: background - - width: parent.width - height: parent.height - - border.width: 2 * __dp - color: root.bgColor - radius: root.radius - border.color: { - if (root.hasFocus) { - if (errorMsg.length > 0) { - return __style.negativeColor - } - else if (warningMsg.length > 0) { - return __style.warningColor - } - return __style.forestColor - } - return __style.transparentColor - } - } - - Row { - height: parent.height - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: root.spacing - anchors.rightMargin: root.spacing - - Item { - id: leftActionContainer - - property bool actionAllowed: leftActionContainer.children.length > 1 - - height: parent.height - width: actionAllowed ? height/2 : 0 - - Item { - width: leftActionContainer.actionAllowed ? parent.width + root.spacing/2 : 0 - height: parent.height - anchors.centerIn: parent - - MouseArea { - anchors.fill: parent - onReleased: root.leftActionClicked() - } - } - } - - Item { - id: contentContainer - - height: parent.height - width: parent.width - (leftActionContainer.actionAllowed ? leftActionContainer.width : 0) - (rightActionContainer.actionAllowed ? rightActionContainer.width : 0) - - MouseArea { - anchors.fill: parent - - onClicked: { - root.contentClicked() - } - } - } - - Item { - id: rightActionContainer - - property bool actionAllowed: rightActionContainer.children.length > 1 - - height: parent.height - width: actionAllowed ? height/2 : 0 - - Item { - width: rightActionContainer.actionAllowed ? parent.width + root.spacing/2 : 0 - height: parent.height - anchors.centerIn: parent - - MouseArea { - anchors.fill: parent - onReleased: root.rightActionClicked() - } - } - } - } - } - - Item { - id: messageItem - - width: parent.width - height: msgRow.height - - Row { - id: msgRow - - spacing: 4 * __dp - - MMIcon { - id: msgIcon - - source: visible ? __style.errorCircleIcon : "" - color: errorMsg.length > 0 ? __style.negativeColor : __style.warningColor - size: __style.icon16 - visible: errorMsg.length > 0 || warningMsg.length > 0 - } - Text { - width: messageItem.width - msgRow.spacing - msgIcon.width - - text: root.errorMsg.length > 0 ? root.errorMsg : root.warningMsg - font: __style.t4 - wrapMode: Text.WordWrap - visible: root.errorMsg.length > 0 || root.warningMsg.length > 0 - } - } - } - } -} diff --git a/app/qml/inputs/MMComboboxInput.qml b/app/qml/inputs/MMComboboxInput.qml index d600ef13e..29daef92c 100644 --- a/app/qml/inputs/MMComboboxInput.qml +++ b/app/qml/inputs/MMComboboxInput.qml @@ -10,15 +10,16 @@ import QtQuick import "../components" as MMComponents +import "../components/private" as MMPrivateComponents /* * Common text input to use in the app. * Disabled state can be achieved by setting `enabled: false`. * - * See MMBaseInput for more properties. + * See MMBaseSingleLineInput for more properties. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property alias loader: drawerLoader @@ -30,34 +31,22 @@ MMBaseInput { property string textRole: "text" property string valueRole: "value" - onRightActionClicked: { drawerLoader.active = true; drawerLoader.focus = true } - onContentClicked: { drawerLoader.active = true; drawerLoader.focus = true } + onTextClicked: { drawerLoader.active = true; drawerLoader.focus = true } + onRightContentClicked: { drawerLoader.active = true; drawerLoader.focus = true } - content: MMComponents.MMText { - id: textField + text: { + if ( !comboboxModel ) return ""; + if ( currentIndex < 0 || currentIndex >= comboboxModel.count ) return ""; - anchors.fill: parent - - font: __style.p5 - - color: root.enabled ? __style.nightColor : __style.mediumGreenColor - - text: { - if ( !comboboxModel ) return ""; - if ( currentIndex < 0 || currentIndex >= comboboxModel.count ) return ""; - - return comboboxModel.get( currentIndex )[textRole] - } + return comboboxModel.get( currentIndex )[textRole] } - rightAction: MMComponents.MMIcon { + rightContent: MMComponents.MMIcon { property bool pressed: false - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 - source: __style.arrowDownIcon - color: root.enabled ? __style.forestColor : __style.mediumGreenColor + source: drawerLoader.active ? __style.arrowUpIcon : __style.arrowDownIcon + color: root.iconColor } Loader { diff --git a/app/qml/inputs/MMPasswordInput.qml b/app/qml/inputs/MMPasswordInput.qml index bf9f1cd2a..bc0850dc9 100644 --- a/app/qml/inputs/MMPasswordInput.qml +++ b/app/qml/inputs/MMPasswordInput.qml @@ -8,46 +8,26 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../components" -MMBaseInput { - id: root - - property alias placeholderText: textField.placeholderText - property alias text: textField.text - - hasFocus: textField.activeFocus +import "../components" as MMComponents +import "../components/private" as MMPrivateComponents - content: TextField { - id: textField +MMPrivateComponents.MMBaseSingleLineInput { + id: root - anchors.fill: parent - anchors.verticalCenter: parent.verticalCenter + textField.echoMode: eyeButton.pressed ? TextInput.Normal : TextInput.Password - color: root.enabled ? __style.nightColor : __style.mediumGreenColor - placeholderTextColor: __style.nightAlphaColor - font: __style.p5 - hoverEnabled: true - echoMode: eyeButton.pressed ? TextInput.Normal : TextInput.Password - inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase - background: Rectangle { - color: __style.transparentColor - } - } + textField.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhHiddenText - rightAction: MMIcon { + rightContent: MMComponents.MMIcon { id: eyeButton property bool pressed: false - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 source: pressed ? __style.hideIcon : __style.showIcon - color: root.enabled ? __style.forestColor : __style.mediumGreenColor + color: root.iconColor } - onRightActionClicked: eyeButton.pressed = !eyeButton.pressed + onRightContentClicked: eyeButton.pressed = !eyeButton.pressed } diff --git a/app/qml/inputs/MMSearchInput.qml b/app/qml/inputs/MMSearchInput.qml index 9f8f0c8e1..ca3842456 100644 --- a/app/qml/inputs/MMSearchInput.qml +++ b/app/qml/inputs/MMSearchInput.qml @@ -8,91 +8,45 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../components" +import "../components" as MMComponents +import "../components/private" as MMPrivateComponents -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root - property alias textFieldComponent: textField - property alias placeholderText: textField.placeholderText - property alias text: textField.text - property bool allowTimer: false + property bool delayedSearch: false property int emitInterval: 200 property bool showClearIcon: true + property string searchText: "" - hasFocus: textField.activeFocus - - signal searchTextChanged( string text ) - - /** - * Used for deactivating focus on MMSearchInput when another component should have focus. - * and the current element's forceActiveFocus() doesnt deactivates SearchBar focus. - */ - function deactivate() { - textField.focus = false - if ( textField.length > 0 ) - textField.clear() - searchTextChanged("") - } - - content: TextField { - id: textField - - anchors.fill: parent - anchors.verticalCenter: parent.verticalCenter - - color: root.enabled ? __style.nightColor : __style.mediumGreenColor - placeholderTextColor: __style.nightAlphaColor - font: __style.p5 - hoverEnabled: true - background: Rectangle { - color: __style.transparentColor - } - - onDisplayTextChanged: { - if ( root.allowTimer ) { - if ( searchTimer.running ) - searchTimer.restart() - else - searchTimer.start() - } - else - { - root.searchTextChanged( textField.displayText ) - } - } - } - - leftAction: MMIcon { + leftContent: MMComponents.MMIcon { id: searchIcon - property bool pressed: false - - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 source: __style.searchIcon - color: root.enabled ? __style.nightColor : __style.mediumGreenColor + color: { + if ( root.editState === "disabled" ) return __style.mediumGreyColor + if ( root.validationState === "error" ) return __style.grapeColor + if ( root.validationState === "warning" ) return __style.earthColor + return __style.nightColor + } } - rightAction: MMIcon { + rightContent: MMComponents.MMIcon { id: rightIcon - anchors.verticalCenter: parent.verticalCenter - size: __style.icon24 source: __style.closeIcon - color: root.enabled ? __style.forestColor : __style.mediumGreenColor - visible: root.showClearIcon && textField.activeFocus && textField.text.length > 0 + color: root.iconColor } - onRightActionClicked: { - if (root.showClearIcon) { + rightContentVisible: root.showClearIcon && textField.activeFocus && root.text.length > 0 + + onRightContentClicked: { + if ( root.showClearIcon ) { textField.clear() - root.searchTextChanged("") + root.searchText = "" } else { // if the clear button should not be there, let's open keyboard instead @@ -100,14 +54,33 @@ MMBaseInput { } } + onTextEdited: { + if ( root.delayedSearch ) { + searchTimer.restart() + } + else + { + root.searchText = root.text + } + } + Timer { id: searchTimer interval: root.emitInterval running: false - onTriggered: { - searchTextChanged( root.text ) - } + onTriggered: root.searchText = root.text + } + + /** + * Used for deactivating focus on MMSearchInput when another component should have focus. + * and the current element's forceActiveFocus() doesnt deactivates SearchBar focus. + */ + function deactivate() { + root.textField.focus = false + if ( root.text.length > 0 ) + root.textField.clear() + root.searchText = "" } } diff --git a/app/qml/inputs/MMSwitchInput.qml b/app/qml/inputs/MMSwitchInput.qml index 913b104f6..a534c22bd 100644 --- a/app/qml/inputs/MMSwitchInput.qml +++ b/app/qml/inputs/MMSwitchInput.qml @@ -8,49 +8,51 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../components" + +import "../components" as MMComponents +import "../components/private" as MMPrivateComponents /* * Common switch input to use in the app. * - * See MMBaseInput for more properties. + * See MMBaseSingleLineInput for more properties. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root - property alias text: textField.text - property alias switchComponent: switchComponent - - hasFocus: textField.activeFocus - - content: TextField { - id: textField + property alias checked: switchComponent.checked + //! emitted when interactively toggled by the user via touch, mouse, or keyboard. + signal toggled() - anchors.fill: parent + textField.readOnly: true - readOnly: true - placeholderTextColor: __style.nightAlphaColor - color: root.enabled ? __style.nightColor : __style.mediumGreenColor + rightContent: MMComponents.MMSwitch { + id: switchComponent - font: __style.p5 - hoverEnabled: true + uncheckedBgColor: { + if ( root.editState !== "enabled" ) return __style.lightGreenColor + if ( root.validationState === "error" ) return __style.negativeLightColor + if ( root.validationState === "warning" ) return __style.sandColor + return __style.lightGreenColor + } - background: Rectangle { - color: __style.transparentColor + checkedBgColor: { + if ( root.editState !== "enabled" ) return __style.mediumGreenColor + if ( root.validationState === "error" ) return __style.negativeColor + if ( root.validationState === "warning" ) return __style.warningColor + return __style.grassColor } - onTextEdited: root.textEdited( textField.text ) + handleColor: root.iconColor } - rightAction: MMSwitch { - id: switchComponent + onTextClicked: toggleSwitchComponent() - anchors.verticalCenter: parent.verticalCenter - x: -20 * __dp // TODO why is this needed? bacause of how baseinput works :( + onRightContentClicked: toggleSwitchComponent() - uncheckedBgColor: __style.lightGreenColor + function toggleSwitchComponent() { + switchComponent.toggle() + toggled() } } diff --git a/app/qml/inputs/MMTextInput.qml b/app/qml/inputs/MMTextInput.qml index 2b030704c..aab67ccac 100644 --- a/app/qml/inputs/MMTextInput.qml +++ b/app/qml/inputs/MMTextInput.qml @@ -8,9 +8,9 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../components" + +import "../components" as MMComponents +import "../components/private" as MMPrivateComponents /* * Common text input to use in the app. @@ -18,58 +18,21 @@ import "../components" * Disabled state can be achieved by setting `enabled: false` * ReadOnly state can be achieved by setting `readOnly: true` * - * See MMBaseInput for more properties. + * See MMBaseSingleLineInput for more properties. */ -MMBaseInput { +MMPrivateComponents.MMBaseSingleLineInput { id: root property bool showClearIcon: true - property alias text: textField.text - property alias placeholderText: textField.placeholderText - property alias readOnly: textField.readOnly - property alias textFieldComponent: textField - - signal textEdited( string text ) - - hasFocus: textField.activeFocus - - content: TextField { - id: textField - - anchors.fill: parent - - placeholderTextColor: __style.nightAlphaColor - color: root.enabled && !readOnly ? __style.nightColor : __style.mediumGreenColor - - font: __style.p5 - hoverEnabled: true - - background: Rectangle { - color: __style.transparentColor - } - - onTextEdited: root.textEdited( textField.text ) - } - - rightAction: MMIcon { - id: rightIcon - - anchors.verticalCenter: parent.verticalCenter + rightContent: MMComponents.MMIcon { size: __style.icon24 source: __style.closeIcon - color: root.enabled && !readOnly ? __style.forestColor : __style.mediumGreenColor - visible: root.showClearIcon && textField.activeFocus && textField.text.length > 0 + color: root.iconColor } - onRightActionClicked: { - if (root.showClearIcon) { - textField.clear() - } - else { - // if the clear button should not be there, let's open keyboard instead - textField.forceActiveFocus() - } - } + rightContentVisible: root.showClearIcon && textField.activeFocus && textField.text.length > 0 + + onRightContentClicked: textField.clear() } diff --git a/app/qml/inputs/MMTextWithButtonInput.qml b/app/qml/inputs/MMTextWithButtonInput.qml deleted file mode 100644 index 36ae9ec0c..000000000 --- a/app/qml/inputs/MMTextWithButtonInput.qml +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic -import "../components" - -/* - * Common text input to use in the app, with button on right - * Disabled state can be achieved by setting `enabled: false`. - * - * See MMBaseInput for more properties. - */ - -MMBaseInput { - id: root - - property alias placeholderText: textField.placeholderText - property alias text: textField.text - property alias buttonText: buttonText.text - property alias buttonEnabled: rightButton.enabled - - signal textEdited( string text ) - signal buttonClicked() - - hasFocus: textField.activeFocus - - content: TextField { - id: textField - - anchors.verticalCenter: parent.verticalCenter - width: parent.width + rightButton.x - - color: root.enabled ? __style.nightColor : __style.mediumGreenColor - placeholderTextColor: __style.nightAlphaColor - font: __style.p5 - hoverEnabled: true - - background: Rectangle { - color: __style.transparentColor - } - - onTextEdited: root.textEdited( textField.text ) - } - - rightAction: Button { - id: rightButton - - property bool transparent: false - - x: 10 * __dp - buttonText.width - height: 34 * __dp - anchors.verticalCenter: parent.verticalCenter - - contentItem: Text { - id: buttonText - - anchors.centerIn: rightButton - font: __style.t3 - color: rightButton.enabled ? rightButton.down || rightButton.hovered ? __style.grassColor : __style.forestColor : __style.deepOceanColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - - background: Rectangle { - color: rightButton.enabled ? rightButton.down || rightButton.hovered ? __style.forestColor : __style.grassColor : __style.mediumGreenColor - radius: height / 2 - } - - onClicked: { - textField.forceActiveFocus() - root.buttonClicked() - } - } -} diff --git a/app/qml/layers/MMFeaturesListPage.qml b/app/qml/layers/MMFeaturesListPage.qml index a3c546e84..c3e654b93 100644 --- a/app/qml/layers/MMFeaturesListPage.qml +++ b/app/qml/layers/MMFeaturesListPage.qml @@ -33,15 +33,15 @@ MMComponents.MMPage { height: parent.height MMSearchInput { - id: searchbox + id: searchBar anchors.top: parent.top anchors.topMargin: __style.spacing20 width: parent.width - allowTimer: true - onSearchTextChanged: featuresModel.searchExpression = searchbox.text + delayedSearch: true + onSearchTextChanged: featuresModel.searchExpression = searchBar.text } ListView { @@ -50,7 +50,7 @@ MMComponents.MMPage { width: parent.width anchors { - top: searchbox.bottom + top: searchBar.bottom bottom: parent.bottom topMargin: __style.spacing20 } diff --git a/app/qml/layers/MMLayerDetailPage.qml b/app/qml/layers/MMLayerDetailPage.qml index bf6c7f497..ae47229cc 100644 --- a/app/qml/layers/MMLayerDetailPage.qml +++ b/app/qml/layers/MMLayerDetailPage.qml @@ -77,17 +77,9 @@ Page { title: qsTr( "Settings" ) text: qsTr( "Visible on map" ) - switchComponent.checked: layerDetailData.isVisible + checked: layerDetailData.isVisible - onContentClicked: { - toggleVisiblity() - } - - onRightActionClicked: { - toggleVisiblity() - } - - function toggleVisiblity() { + onToggled: { __activeProject.switchLayerTreeNodeVisibility( layerDetailData.layerTreeNode ) } } diff --git a/app/qml/layers/MMLayersController.qml b/app/qml/layers/MMLayersController.qml index 4a98eed31..8b7d5487c 100644 --- a/app/qml/layers/MMLayersController.qml +++ b/app/qml/layers/MMLayersController.qml @@ -102,7 +102,7 @@ Item { } } - onSearchboxClicked: function() { + onSearchBarClicked: function() { let item = pagesStackView.push( searchLayersPage, {}, StackView.Immediate ) item.forceActiveFocus() } diff --git a/app/qml/layers/MMLayersListPage.qml b/app/qml/layers/MMLayersListPage.qml index 690f75c6e..7700b010c 100644 --- a/app/qml/layers/MMLayersListPage.qml +++ b/app/qml/layers/MMLayersListPage.qml @@ -22,7 +22,7 @@ MMComponents.MMPage { signal nodeClicked( var node, string nodeType, string nodeName ) signal nodeVisibilityClicked( var node ) - signal searchboxClicked() + signal searchBarClicked() pageHeader.title: root.pageTitle @@ -33,7 +33,7 @@ MMComponents.MMPage { height: parent.height MMSearchInput { - id: searchbox + id: searchBar anchors.top: parent.top anchors.topMargin: __style.spacing20 @@ -43,7 +43,7 @@ MMComponents.MMPage { MouseArea { anchors.fill: parent - onClicked: root.searchboxClicked() + onClicked: root.searchBarClicked() } } @@ -53,7 +53,7 @@ MMComponents.MMPage { width: parent.width anchors { - top: searchbox.bottom + top: searchBar.bottom topMargin: __style.spacing20 bottom: parent.bottom } diff --git a/app/qml/layers/MMLayersListSearchPage.qml b/app/qml/layers/MMLayersListSearchPage.qml index 9a8a2a214..9fbdb976f 100644 --- a/app/qml/layers/MMLayersListSearchPage.qml +++ b/app/qml/layers/MMLayersListSearchPage.qml @@ -33,15 +33,13 @@ MMComponents.MMPage { height: parent.height MMSearchInput { - id: searchbox + id: searchBar anchors.top: parent.top anchors.topMargin: __style.spacing20 width: parent.width - onSearchTextChanged: function( searchText ) { - root.searchTextChanged( searchText ) - } + onSearchTextChanged: root.searchTextChanged( searchBar.searchText ) } MMLayersList { @@ -50,7 +48,7 @@ MMComponents.MMPage { width: parent.width anchors { - top: searchbox.bottom + top: searchBar.bottom topMargin: __style.spacing20 bottom: parent.bottom } @@ -76,5 +74,5 @@ MMComponents.MMPage { } // open keyboard automatically - Component.onCompleted: searchbox.textFieldComponent.forceActiveFocus() + Component.onCompleted: searchBar.textField.forceActiveFocus() } diff --git a/app/qml/project/MMProjectHomeTab.qml b/app/qml/project/MMProjectHomeTab.qml index 862081bf9..5ffd3b826 100644 --- a/app/qml/project/MMProjectHomeTab.qml +++ b/app/qml/project/MMProjectHomeTab.qml @@ -40,16 +40,13 @@ Item { width: parent.width - 2 * root.padding placeholderText: qsTr("Search for projects...") - onSearchTextChanged: function(text) { - } - anchors { top: parent.top left: parent.left right: parent.right } - allowTimer: true + delayedSearch: true } MMInfoBox { @@ -99,7 +96,10 @@ Item { projectModelType: MM.ProjectsModel.LocalProjectsModel activeProjectId: root.activeProjectId - searchText: searchBar.text + + hideActiveProject: true // TODO: do not hide when searching! + searchText: searchBar.searchText + spacing: root.spacing anchors { diff --git a/app/qml/project/MMProjectServerTab.qml b/app/qml/project/MMProjectServerTab.qml index f56097cdb..aafbf9617 100644 --- a/app/qml/project/MMProjectServerTab.qml +++ b/app/qml/project/MMProjectServerTab.qml @@ -43,7 +43,7 @@ Item { right: parent.right } - allowTimer: true + delayedSearch: true } @@ -52,7 +52,7 @@ Item { projectModelType: root.projectModelType activeProjectId: root.activeProjectId - searchText: searchBar.text + searchText: searchBar.searchText spacing: root.spacing anchors { diff --git a/app/qml/project/components/MMProjectDelegate.qml b/app/qml/project/components/MMProjectDelegate.qml index 3607acf7e..02a3e1f6f 100644 --- a/app/qml/project/components/MMProjectDelegate.qml +++ b/app/qml/project/components/MMProjectDelegate.qml @@ -98,7 +98,7 @@ Control { font: __style.t3 color: { if ( root.projectIsOpened ) return __style.polarColor - if ( root.state === "Error" ) return __style.nightAlphaColor + if ( root.state === "Error" ) return __style.darkGreyColor return __style.nightColor } } diff --git a/app/qml/project/components/MMProjectWizardDelegate.qml b/app/qml/project/components/MMProjectWizardDelegate.qml index bb99eb57e..b892c3a2e 100644 --- a/app/qml/project/components/MMProjectWizardDelegate.qml +++ b/app/qml/project/components/MMProjectWizardDelegate.qml @@ -41,7 +41,7 @@ Item { Layout.fillHeight: true Layout.fillWidth: true - onTextChanged: root.attrNameChanged( text ) + onTextEdited: (text) => root.attrNameChanged( text ) } MMInputs.MMComboboxInput { diff --git a/app/qml/settings/components/MMSettingsInput.qml b/app/qml/settings/components/MMSettingsInput.qml index 3b2427075..f76df5c1b 100644 --- a/app/qml/settings/components/MMSettingsInput.qml +++ b/app/qml/settings/components/MMSettingsInput.qml @@ -51,10 +51,10 @@ MMSettingsItem { title: root.title - bgColor: __style.lightGreenColor + textFieldBackground.color: __style.lightGreenColor text: root.value - textFieldComponent.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + textField.inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase } MMComponents.MMListSpacer { height: __style.spacing40 } diff --git a/gallery/qml.qrc b/gallery/qml.qrc index 6b3519e5b..24feb29bb 100644 --- a/gallery/qml.qrc +++ b/gallery/qml.qrc @@ -54,6 +54,8 @@ ../app/qml/components/MMToolbarButton.qml ../app/qml/components/MMBadge.qml ../app/qml/components/MMToolbar.qml + ../app/qml/components/private/MMBaseInput.qml + ../app/qml/components/private/MMBaseSingleLineInput.qml ../app/qml/components/private/MMToolbarShortButton.qml ../app/qml/components/private/MMToolbarLongButton.qml ../app/qml/components/MMListDelegate.qml @@ -61,7 +63,6 @@ ../app/qml/components/MMListFooterSpacer.qml ../app/qml/components/MMListMultiselectDrawer.qml ../app/qml/components/MMListSpacer.qml - ../app/qml/components/MMMorePhoto.qml ../app/qml/components/MMPhoto.qml ../app/qml/components/MMCheckBox.qml ../app/qml/components/MMHlineText.qml @@ -74,8 +75,6 @@ ../app/qml/components/MMDrawer.qml ../app/qml/components/MMDrawerHeader.qml ../app/qml/components/MMBusyIndicator.qml - ../app/qml/components/MMPhotoAttachment.qml - ../app/qml/components/MMPhotoPreview.qml ../app/qml/account/components/MMAccountPageItem.qml ../app/qml/account/components/MMIconCheckBoxHorizontal.qml ../app/qml/account/components/MMIconCheckBoxVertical.qml @@ -88,13 +87,11 @@ ../app/qml/account/MMLoginPage.qml ../app/qml/account/MMSignUpPage.qml ../app/qml/account/MMWhichIndustryPage.qml - ../app/qml/inputs/MMBaseInput.qml ../app/qml/inputs/MMComboboxInput.qml ../app/qml/inputs/MMPasswordInput.qml ../app/qml/inputs/MMTextInput.qml ../app/qml/inputs/MMSearchInput.qml ../app/qml/inputs/MMSwitchInput.qml - ../app/qml/inputs/MMTextWithButtonInput.qml ../app/qml/form/components/MMFormTabBar.qml ../app/qml/form/components/MMCalendarDrawer.qml ../app/qml/form/components/MMFeaturesListPageDrawer.qml @@ -105,6 +102,8 @@ ../app/qml/form/components/calendar/MMTumbler.qml ../app/qml/form/components/calendar/MMTimeTumbler.qml ../app/qml/form/components/calendar/MMDateTumbler.qml + ../app/qml/form/components/photo/MMPhotoAttachment.qml + ../app/qml/form/components/photo/MMPhotoPreview.qml ../app/qml/form/editors/MMFormCalendarEditor.qml ../app/qml/form/editors/MMFormComboboxBaseEditor.qml ../app/qml/form/editors/MMFormGalleryEditor.qml diff --git a/gallery/qml/pages/ChecksPage.qml b/gallery/qml/pages/ChecksPage.qml index a36eb58ff..9e097a4b1 100644 --- a/gallery/qml/pages/ChecksPage.qml +++ b/gallery/qml/pages/ChecksPage.qml @@ -188,6 +188,11 @@ Column { enabled: false checked: false } + MMComponents.MMSwitch { + text: "disabled" + enabled: false + checked: true + } } } } diff --git a/gallery/qml/pages/ColorsPage.qml b/gallery/qml/pages/ColorsPage.qml index 0dbd48ccc..9fe50f2ab 100644 --- a/gallery/qml/pages/ColorsPage.qml +++ b/gallery/qml/pages/ColorsPage.qml @@ -151,6 +151,14 @@ ScrollView { text: "negativeColor" color: __style.negativeColor } + GalleryComponents.ColorBox { + text: "negativeLightColor" + color: __style.negativeLightColor + } + GalleryComponents.ColorBox { + text: "negativeUltraLightColor" + color: __style.negativeUltraLightColor + } GalleryComponents.ColorBox { text: "informativeColor" color: __style.informativeColor @@ -178,14 +186,6 @@ ScrollView { columns: 3 spacing: 20 anchors.fill: parent - GalleryComponents.ColorBox { - text: "nightAlphaColor" - color: __style.nightAlphaColor - } - GalleryComponents.ColorBox { - text: "errorBgInputColor" - color: __style.errorBgInputColor - } GalleryComponents.ColorBox { text: "shadowColor" color: __style.shadowColor diff --git a/gallery/qml/pages/EditorsPage.qml b/gallery/qml/pages/EditorsPage.qml index 2faac100d..f571a6c89 100644 --- a/gallery/qml/pages/EditorsPage.qml +++ b/gallery/qml/pages/EditorsPage.qml @@ -143,7 +143,7 @@ ScrollView { height: numberEditor.height fieldValue: "2" - fieldConfig: ({Min: 1.0, Max: 3.0, Precition: 1, Suffix: "s.", Step: 0.1}) + fieldConfig: ({Min: 1.0, Max: 3.0, Precision: 1, Suffix: "s.", Step: 0.1}) fieldTitle: "MMFormNumberEditor" MMFormEditors.MMFormNumberEditor { @@ -183,7 +183,8 @@ ScrollView { width: parent.width onTrashClicked: console.log("Move to trash") - onContentClicked: console.log("Open photo") + onChooseFromGalleryClicked: console.log("Choose from gallery") + onCapturePhotoClicked: console.log("Capture photo") } } @@ -192,12 +193,11 @@ ScrollView { height: textMultilineEditor.height fieldTitle: "MMFormTextMultilineEditor" + fieldValue: "See something powerfull Mergin Maps documentation, for more information continue here." MMFormEditors.MMFormTextMultilineEditor { id: textMultilineEditor - placeholderText: "Place for multi row text" width: parent.width - text: "See something powerfull Mergin Maps documentation, for more information continue here." } } @@ -228,11 +228,8 @@ ScrollView { MMFormEditors.MMFormCalendarEditor { id: dateTimeCalendar - title: "Date & Time" text: "yyyy-MM-dd hh:mm" - enabled: checkbox.checked width: parent.width - warningMsg: text.length > 0 ? "" : "Press button to open Calendar" fieldIsDate: false includesTime: true @@ -256,7 +253,6 @@ ScrollView { MMFormEditors.MMFormCalendarEditor { id: dateCalendar - title: "Date" text: "yyyy-MM-dd" enabled: checkbox.checked width: parent.width @@ -283,7 +279,7 @@ ScrollView { MMFormEditors.MMFormCalendarEditor { id: timeCalendar - title: "Time" + text: "hh:mm" enabled: checkbox.checked width: parent.width @@ -315,22 +311,16 @@ ScrollView { } } - - Label { - text: "MMFormRichTextViewer - Text" - } - GalleryComponents.EditorItem { width: parent.width height: richTextViewer.height - fieldValue: "Lorem ipsum dolor sit amet, consectetur adipiscing elit," + - " sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\n\n" + - " Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris" + - " nisi ut aliquip ex ea commodo consequat." + fieldValue: "

Lorem ipsum

dolor sit amet, consectetur adipiscing elit,\n" + + " sed do eiusmod tempor \n\nincididunt ut labore et dolore magna aliqua.


" + + "See something powerfull Mergin Maps documentation, for more information continue here." fieldConfig: ({UseHtml: false}) - fieldTitle: "" + fieldTitle: "MMFormRichTextViewer - Text" MMFormEditors.MMFormRichTextViewer { id: richTextViewer @@ -338,8 +328,21 @@ ScrollView { } } - Label { - text: "MMFormValueMapEditor - Value map" + GalleryComponents.EditorItem { + width: parent.width + height: richTextViewerHtml.height + + fieldValue: "

Lorem ipsum

dolor sit amet, consectetur adipiscing elit,\n" + + " sed do eiusmod tempor \n\nincididunt ut labore et dolore magna aliqua.


" + + "See something powerfull Mergin Maps documentation, for more information continue here." + + fieldConfig: ({UseHtml: true}) + fieldTitle: "MMFormRichTextViewer - HTML" + + MMFormEditors.MMFormRichTextViewer { + id: richTextViewerHtml + width: parent.width + } } GalleryComponents.EditorItem { @@ -348,7 +351,7 @@ ScrollView { fieldValue: "0" fieldConfig: ({ map: [ {"First": 0}, {"Second": 1} ] }) - fieldTitle: "Value map" + fieldTitle: "MMFormValueMapEditor - Value map" MMFormEditors.MMFormValueMapEditor { id: valueMapEditor diff --git a/gallery/qml/pages/InputsPage.qml b/gallery/qml/pages/InputsPage.qml index 375ec8fd5..0cf9030ec 100644 --- a/gallery/qml/pages/InputsPage.qml +++ b/gallery/qml/pages/InputsPage.qml @@ -90,15 +90,6 @@ ScrollView { warningMsg: text.length > 0 ? "" : "Write something" } - MMInputs.MMTextWithButtonInput { - title: "MMTextWithButtonInput" - placeholderText: "Write something" - buttonText: "Copy" - width: parent.width - onButtonClicked: console.log("Copy pressed") - buttonEnabled: text.length > 0 - } - MMInputs.MMPasswordInput { title: "MMPasswordInput" text: "Password" diff --git a/gallery/qml/pages/NotificationPage.qml b/gallery/qml/pages/NotificationPage.qml index d9bcb7788..4cc27a453 100644 --- a/gallery/qml/pages/NotificationPage.qml +++ b/gallery/qml/pages/NotificationPage.qml @@ -12,63 +12,69 @@ import QtQuick.Controls import QtQuick.Controls.Basic import "../../app/qml/components" +import "../../app/qml/components/private" as MMPrivateComponents import "../../app/qml/inputs" + import mm 1.0 as MM Page { id: pane - Rectangle { - anchors.fill: parent - color: "white" + background: Rectangle { + color: __style.lightGreenColor } - Column { - width: parent.width - 60 - spacing: 20 - anchors.centerIn: parent + contentItem: Item { + width: ( ApplicationWindow.window?.width ?? 0 ) - MMTextWithButtonInput { - buttonText: "Send" - anchors.horizontalCenter: parent.horizontalCenter - text: "Write an informative message" + Column { + width: parent.width * 2/3 + spacing: 20 + x: parent.width / 2 - width / 2 - onButtonClicked: { __notificationModel.addInfo(text) } - } - MMTextWithButtonInput { - buttonText: "Send" - anchors.horizontalCenter: parent.horizontalCenter - text: "Write a success message" + MMPrivateComponents.MMBaseSingleLineInput { + width: parent.width + text: "Write an informative message" - onButtonClicked: { __notificationModel.addSuccess(text) } - } - MMTextWithButtonInput { - buttonText: "Send" - anchors.horizontalCenter: parent.horizontalCenter - text: "Write a warning message" + rightContent: MMIcon { source: __style.checkmarkIcon } + onRightContentClicked: { __notificationModel.addInfo(text) } + } - onButtonClicked: { __notificationModel.addWarning(text) } - } - MMTextWithButtonInput { - buttonText: "Send" - anchors.horizontalCenter: parent.horizontalCenter - text: "Write an error message" + MMPrivateComponents.MMBaseSingleLineInput { + width: parent.width + text: "Write a success message" - onButtonClicked: { __notificationModel.addError(text) } - } - MMTextWithButtonInput { - 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!“ " + rightContent: MMIcon { source: __style.checkmarkIcon } + onRightContentClicked: { __notificationModel.addSuccess(text) } + } + MMPrivateComponents.MMBaseSingleLineInput { + width: parent.width + text: "Write a warning message" - onButtonClicked: { __notificationModel.addInfo(text) } - } - MMTextWithButtonInput { - buttonText: "Send" - anchors.horizontalCenter: parent.horizontalCenter - text: "Click on notification to invoke action" + rightContent: MMIcon { source: __style.checkmarkIcon } + onRightContentClicked: { __notificationModel.addWarning(text) } + } + MMPrivateComponents.MMBaseSingleLineInput { + width: parent.width + text: "Write an error message" + + rightContent: MMIcon { source: __style.checkmarkIcon } + onRightContentClicked: { __notificationModel.addError(text) } + } + MMPrivateComponents.MMBaseSingleLineInput { + width: parent.width + 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!“ " + + rightContent: MMIcon { source: __style.checkmarkIcon } + onRightContentClicked: { __notificationModel.addInfo(text) } + } + MMPrivateComponents.MMBaseSingleLineInput { + width: parent.width + text: "Click on notification to invoke action" - onButtonClicked: { __notificationModel.addWarning(text, MM.NotificationType.ShowProjectIssuesAction) } + rightContent: MMIcon { source: __style.checkmarkIcon } + onRightContentClicked: { __notificationModel.addWarning(text, MM.NotificationType.ShowProjectIssuesAction) } + } } } diff --git a/gallery/qml/pages/PhotosPage.qml b/gallery/qml/pages/PhotosPage.qml index 02d0858da..65f06f7fc 100644 --- a/gallery/qml/pages/PhotosPage.qml +++ b/gallery/qml/pages/PhotosPage.qml @@ -42,20 +42,20 @@ Page { width: parent.width photoUrl: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - state: "valid" + photoState: "valid" } MMFormPhotoViewer { width: 200 photoUrl: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - state: "valid" + photoState: "valid" } MMFormPhotoViewer { width: 200 - state: "notAvailable" + photoState: "notAvailable" } } }