From 14f11251e22868ad0e03815c81dc78d229d39800 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 10 Feb 2026 18:57:06 +0200 Subject: [PATCH 1/9] Created photo card component Added page with photo card component Modified form gallery editor --- app/qml/CMakeLists.txt | 1 + app/qml/components/MMPhotoCard.qml | 97 ++++++++++++++++++++ app/qml/form/editors/MMFormGalleryEditor.qml | 13 ++- gallery/qml.qrc | 1 + gallery/qml/Main.qml | 4 + gallery/qml/pages/PhotoCardPage.qml | 91 ++++++++++++++++++ 6 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 app/qml/components/MMPhotoCard.qml create mode 100644 gallery/qml/pages/PhotoCardPage.qml diff --git a/app/qml/CMakeLists.txt b/app/qml/CMakeLists.txt index d9662b019..6eef31f76 100644 --- a/app/qml/CMakeLists.txt +++ b/app/qml/CMakeLists.txt @@ -44,6 +44,7 @@ set(MM_QML components/MMColorButton.qml components/MMPopup.qml components/MMPhoto.qml + components/MMPhotoCard.qml components/MMProgressBar.qml components/MMRadioButton.qml components/MMRoundButton.qml diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml new file mode 100644 index 000000000..74dfe1d36 --- /dev/null +++ b/app/qml/components/MMPhotoCard.qml @@ -0,0 +1,97 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +import QtQuick +import QtQuick.Controls +import Qt5Compat.GraphicalEffects + +Item { + id: root + + property alias imageSource: bngImage.photoUrl + property string text: "" + property bool textVisible: true + + signal clicked(var path) + + width: 300 + height: 300 + Rectangle { + id: maskRect + anchors.fill: parent + radius: __style.margin20 + visible: false + } + + Item { + anchors.fill: parent + layer.enabled: true + layer.effect: OpacityMask { + maskSource: maskRect + } + + MMPhoto { + id: bngImage + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + autoTransform: true + smooth: true + } + + FastBlur { + id: footer + visible: root.textVisible + + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: parent.height * 0.33 + + radius: 40 + source: ShaderEffectSource { + sourceItem: bngImage + sourceRect: Qt.rect(0, bngImage.height - footer.height, bngImage.width, + footer.height) + recursive: false + } + + // Tint Overlay (Child of FastBlur) + Rectangle { + anchors.fill: parent + color: __style.nightColor + opacity: 0.35 + } + + Text { + text: root.text + anchors.centerIn: parent + width: parent.width - __style.margin32 + height: parent.height - __style.margin10 + + color: __style.polarColor + font { + pixelSize: __style.margin10 + bold: true + family: "Inter" + } + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + elide: Text.ElideRight + } + } + } + + MMSingleClickMouseArea { + anchors.fill: parent + onSingleClicked: root.clicked(root.imageSource) + } +} diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index 656c51ead..6af735864 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -48,20 +48,23 @@ MMPrivateComponents.MMBaseInput { homePath: root._fieldActiveProject.homePath } - delegate: MMComponents.MMPhoto { + delegate: MMComponents.MMPhotoCard{ width: rowView.height + height: rowView.height - fillMode: Image.PreserveAspectCrop - - photoUrl: { + imageSource: { let absolutePath = model.PhotoPath if ( absolutePath !== '' && __inputUtils.fileExists( absolutePath ) ) { - return "file://" + absolutePath + return "file:///" + absolutePath } return '' } + textVisible: model.FeatureTitle ? true : false + + text: model.FeatureTitle + onClicked: function( path ) { root.openLinkedFeature( model.FeaturePair ) } diff --git a/gallery/qml.qrc b/gallery/qml.qrc index 9d82ad2d8..1e09e70f3 100644 --- a/gallery/qml.qrc +++ b/gallery/qml.qrc @@ -18,6 +18,7 @@ qml/pages/NotificationPage.qml qml/pages/DrawerPage.qml qml/pages/PagesPage.qml + qml/pages/PhotoCardPage.qml qml/pages/ChecksPage.qml qml/pages/MapPage.qml qml/pages/ToolbarPage.qml diff --git a/gallery/qml/Main.qml b/gallery/qml/Main.qml index 854b5d21b..70eb76f6b 100644 --- a/gallery/qml/Main.qml +++ b/gallery/qml/Main.qml @@ -229,6 +229,10 @@ ApplicationWindow { title: "Photos" source: "PhotosPage.qml" } + ListElement { + title: "Photo cards page" + source: "PhotoCardPage.qml" + } ListElement { title: "Map" source: "MapPage.qml" diff --git a/gallery/qml/pages/PhotoCardPage.qml b/gallery/qml/pages/PhotoCardPage.qml new file mode 100644 index 000000000..6ae9c0934 --- /dev/null +++ b/gallery/qml/pages/PhotoCardPage.qml @@ -0,0 +1,91 @@ + + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +import "../../app/qml/components" +import "../../app/qml/form/editors" + +Page { + + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + ScrollView { + spacing: 20 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 20 + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.fill: parent + anchors.rightMargin: 20 + width: parent.width - 30 + height: contentHeight + + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + + Row { + spacing: 10 + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: false + } + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "/Users/gabrielbolbotina/development/repos/mm/build-macos/app/android/assets/qgis-data/projects/Timisoara-trees/20260204_171556.jpg" + text: "This is my feature and this is how it looks until now" + textVisible: true + } + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: false + } + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "/Users/gabrielbolbotina/development/repos/mm/build-macos/app/android/assets/qgis-data/projects/Timisoara-trees/20260204_171556.jpg" + text: "This is my feature and I am proud of it to be shown in this state" + textVisible: true + } + } + } +} From 188dc399a0add6de148324c6cc3c84de09adda93 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Thu, 12 Feb 2026 14:16:12 +0200 Subject: [PATCH 2/9] Made changes to the photo card component Added larger cards for gallery page --- app/qml/components/MMPhotoCard.qml | 23 +-- app/qml/form/editors/MMFormGalleryEditor.qml | 2 +- gallery/qml/pages/PhotoCardPage.qml | 149 +++++++++++-------- 3 files changed, 96 insertions(+), 78 deletions(-) diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index 74dfe1d36..0f72981f8 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -17,10 +17,11 @@ Item { property string text: "" property bool textVisible: true - signal clicked(var path) + signal clicked(string path) + + width: 120 + height: 120 - width: 300 - height: 300 Rectangle { id: maskRect anchors.fill: parent @@ -43,6 +44,8 @@ Item { smooth: true } + // the footer of the card + // contains the text to display over the blurred part of the image FastBlur { id: footer visible: root.textVisible @@ -52,7 +55,7 @@ Item { right: parent.right bottom: parent.bottom } - height: parent.height * 0.33 + height: parent.height * 0.35 radius: 40 source: ShaderEffectSource { @@ -71,16 +74,14 @@ Item { Text { text: root.text + width: root.width - 2 * __style.margin12 + height: 2 * __style.margin14 anchors.centerIn: parent - width: parent.width - __style.margin32 - height: parent.height - __style.margin10 color: __style.polarColor - font { - pixelSize: __style.margin10 - bold: true - family: "Inter" - } + font: __style.p5 + lineHeightMode: Text.FixedHeight + lineHeight: 16 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index 6af735864..7f0c0d917 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -61,7 +61,7 @@ MMPrivateComponents.MMBaseInput { return '' } - textVisible: model.FeatureTitle ? true : false + textVisible: true text: model.FeatureTitle diff --git a/gallery/qml/pages/PhotoCardPage.qml b/gallery/qml/pages/PhotoCardPage.qml index 6ae9c0934..a648ab2fd 100644 --- a/gallery/qml/pages/PhotoCardPage.qml +++ b/gallery/qml/pages/PhotoCardPage.qml @@ -17,75 +17,92 @@ import "../../app/qml/form/editors" Page { - Rectangle { - anchors.fill: parent - color: __style.lightGreenColor + Rectangle { + anchors.fill: parent + color: __style.lightGreenColor + } + + Column { + + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 20 + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.fill: parent + anchors.rightMargin: 20 + + ScrollView { + spacing: 20 + width: parent.width - 30 + height: contentHeight + + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + + ScrollBar.horizontal.visible: false + + Row { + spacing: 10 + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature and added longer text to check how it looks" + textVisible: true + } + + MMPhotoCard { + width: 120 + height: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: false + } + } + } + + MMListSpacer { + height: __style.margin20 } ScrollView { - spacing: 20 - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: 20 - anchors.left: parent.left - anchors.leftMargin: 20 - anchors.fill: parent - anchors.rightMargin: 20 - width: parent.width - 30 - height: contentHeight - - ScrollBar.vertical.policy: ScrollBar.AlwaysOff - ScrollBar.horizontal.policy: ScrollBar.AlwaysOn - - Row { - spacing: 10 - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: true - } - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: false - } - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "/Users/gabrielbolbotina/development/repos/mm/build-macos/app/android/assets/qgis-data/projects/Timisoara-trees/20260204_171556.jpg" - text: "This is my feature and this is how it looks until now" - textVisible: true - } - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: true - } - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: false - } - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "/Users/gabrielbolbotina/development/repos/mm/build-macos/app/android/assets/qgis-data/projects/Timisoara-trees/20260204_171556.jpg" - text: "This is my feature and I am proud of it to be shown in this state" - textVisible: true - } + spacing: 20 + width: parent.width - 30 + height: contentHeight + + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + ScrollBar.horizontal.visible: false + + Row { + spacing: 10 + + MMPhotoCard { + width: 180 + height: 180 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } + + MMPhotoCard { + width: 180 + height: 180 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature and added longer text to check how it looks" + textVisible: true } + } } + } } From a5c1b748256320eee79ed70dc76c904015c47494 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Thu, 12 Feb 2026 15:18:20 +0200 Subject: [PATCH 3/9] Modified the font and proportions --- app/qml/components/MMPhotoCard.qml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index 0f72981f8..b4dcb9d4e 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -55,9 +55,9 @@ Item { right: parent.right bottom: parent.bottom } - height: parent.height * 0.35 + height: parent.height * 0.33 - radius: 40 + radius: 20 source: ShaderEffectSource { sourceItem: bngImage sourceRect: Qt.rect(0, bngImage.height - footer.height, bngImage.width, @@ -74,17 +74,19 @@ Item { Text { text: root.text - width: root.width - 2 * __style.margin12 - height: 2 * __style.margin14 + width: root.width - 2 * __style.margin16 anchors.centerIn: parent color: __style.polarColor - font: __style.p5 + font: __style.t5 + lineHeightMode: Text.FixedHeight - lineHeight: 16 + lineHeight: __style.margin16 + maximumLineCount: 2 - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignHCenter | Text.AlignJustify verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap elide: Text.ElideRight } From 58548aa7a2b67bad532fc05c4cea3da2ddfdbcd1 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 17 Feb 2026 14:53:18 +0200 Subject: [PATCH 4/9] Fixed Photo page and added the photoCard Added size property Modified text padding to adapt to the size Added photo card in gallery that adapts to the width --- app/qml/components/MMPhotoCard.qml | 7 +- app/qml/form/editors/MMFormGalleryEditor.qml | 3 +- gallery/qml.qrc | 3 +- gallery/qml/components/FormPhotoViewer.qml | 170 +++++++++++++++++++ gallery/qml/pages/PhotoCardPage.qml | 108 ------------ gallery/qml/pages/PhotosPage.qml | 159 ++++++++++++++--- 6 files changed, 311 insertions(+), 139 deletions(-) create mode 100644 gallery/qml/components/FormPhotoViewer.qml delete mode 100644 gallery/qml/pages/PhotoCardPage.qml diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index b4dcb9d4e..5e15f58f7 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -16,11 +16,12 @@ Item { property alias imageSource: bngImage.photoUrl property string text: "" property bool textVisible: true + property int size: 120 signal clicked(string path) - width: 120 - height: 120 + height: size + width: size Rectangle { id: maskRect @@ -74,7 +75,7 @@ Item { Text { text: root.text - width: root.width - 2 * __style.margin16 + width: root.width - 2 * (root.size * 0.15) anchors.centerIn: parent color: __style.polarColor diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index 7f0c0d917..d3a519d24 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -49,8 +49,7 @@ MMPrivateComponents.MMBaseInput { } delegate: MMComponents.MMPhotoCard{ - width: rowView.height - height: rowView.height + size: rowView.height imageSource: { let absolutePath = model.PhotoPath diff --git a/gallery/qml.qrc b/gallery/qml.qrc index 1e09e70f3..d709e4bc5 100644 --- a/gallery/qml.qrc +++ b/gallery/qml.qrc @@ -5,6 +5,7 @@ qml/components/EditorItem.qml qml/components/TextItem.qml qml/components/ColorBox.qml + qml/components/FormPhotoViewer.qml qml/pages/ComponentsPage.qml qml/pages/SandboxPage.qml qml/pages/IconsPage.qml @@ -18,7 +19,6 @@ qml/pages/NotificationPage.qml qml/pages/DrawerPage.qml qml/pages/PagesPage.qml - qml/pages/PhotoCardPage.qml qml/pages/ChecksPage.qml qml/pages/MapPage.qml qml/pages/ToolbarPage.qml @@ -107,6 +107,7 @@ ../app/qml/form/components/calendar/MMDateTumbler.qml ../app/qml/form/components/photo/MMPhotoAttachment.qml ../app/qml/form/components/photo/MMPhotoPreview.qml + ../app/qml/form/components/MMFormPhotoSketchingPageDialog.qml ../app/qml/form/editors/MMFormCalendarEditor.qml ../app/qml/form/editors/MMFormComboboxBaseEditor.qml ../app/qml/form/editors/MMFormGalleryEditor.qml diff --git a/gallery/qml/components/FormPhotoViewer.qml b/gallery/qml/components/FormPhotoViewer.qml new file mode 100644 index 000000000..27cf54506 --- /dev/null +++ b/gallery/qml/components/FormPhotoViewer.qml @@ -0,0 +1,170 @@ + + +/*************************************************************************** + * * + * 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 mm 1.0 as MM + +import "../../app/qml/components" as MMComponents +import "../../app/qml/components/private" as MMPrivateComponents +import "../../app/qml/form/components/photo" as MMPhotoComponents +import "../../app/qml/form/components" as MMFormComponents + + +/* + * Photo viewer mock for the gallery feature form. + * Its purpose is to show image based on the provided URL. + * It is not combined with MMPhotoFormEditor as it would be not possible to use it in the gallery then. + * + * Serves as a base class for MMPhotoFormEditor. + */ +MMPrivateComponents.MMBaseInput { + id: root + + property string photoUrl: "" + property bool hasCameraCapability: true + + property var photoComponent: photo + property alias photoState: photoStateGroup.state + + signal trashClicked + signal capturePhotoClicked + signal chooseFromGalleryClicked + signal drawOnPhotoClicked + + StateGroup { + id: photoStateGroup + + states: [ + State { + name: "valid" + }, + State { + name: "notSet" + }, + State { + name: "notAvailable" + } + ] + + state: "notSet" + } + + inputContent: Rectangle { + width: parent.width + height: __style.row160 + + color: __style.polarColor + radius: __style.radius20 + + MMComponents.MMPhoto { + id: photo + + width: parent.width + height: parent.height + + visible: photoStateGroup.state !== "notSet" + + photoUrl: root.photoUrl + isLocalFile: root.photoUrl.startsWith("file://") + cache: false + + fillMode: Image.PreserveAspectCrop + + onStatusChanged: { + if (status === Image.Error) { + __inputUtils.log( + "Image Loading", + "Could not load the image. It may be missing or invalid, the URL might be incorrect, or there may be no network connection: " + root.photoUrl) + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (photo.status === Image.Ready) { + previewLoader.active = true + previewLoader.focus = true + } + } + } + + MMComponents.MMRoundButton { + anchors { + right: parent.right + bottom: parent.bottom + rightMargin: __style.margin10 + bottomMargin: __style.margin10 + } + + bgndColor: __style.negativeColor + iconSource: __style.deleteIcon + iconColor: __style.grapeColor + + visible: root.editState === "enabled" + && photoStateGroup.state !== "notSet" + + onClicked: root.trashClicked() + } + + MMComponents.MMRoundButton { + anchors { + right: parent.right + top: parent.top + rightMargin: __style.margin10 + topMargin: __style.margin10 + } + + bgndColor: __style.lightGreenColor + iconSource: __style.drawIcon + iconColor: __style.forestColor + + visible: root.editState === "enabled" + && photoStateGroup.state !== "notSet" + && __activeProject.photoSketchingEnabled + && root.photoUrl.startsWith("file://") + + onClicked: { + sketchingLoader.active = true + sketchingLoader.focus = true + } + } + } + + MMPhotoComponents.MMPhotoAttachment { + width: parent.width + height: parent.height + + visible: photoStateGroup.state === "notSet" + enabled: root.editState === "enabled" + + hasCameraCapability: root.hasCameraCapability + + onCapturePhotoClicked: root.capturePhotoClicked() + onChooseFromGalleryClicked: root.chooseFromGalleryClicked() + } + } + + Loader { + id: previewLoader + + asynchronous: true + active: false + sourceComponent: previewComponent + } + + Component { + id: previewComponent + + MMPhotoComponents.MMPhotoPreview { + photoUrl: root.photoUrl + } + } +} diff --git a/gallery/qml/pages/PhotoCardPage.qml b/gallery/qml/pages/PhotoCardPage.qml deleted file mode 100644 index a648ab2fd..000000000 --- a/gallery/qml/pages/PhotoCardPage.qml +++ /dev/null @@ -1,108 +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 "../../app/qml/components" -import "../../app/qml/form/editors" - -Page { - - Rectangle { - anchors.fill: parent - color: __style.lightGreenColor - } - - Column { - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: 20 - anchors.left: parent.left - anchors.leftMargin: 20 - anchors.fill: parent - anchors.rightMargin: 20 - - ScrollView { - spacing: 20 - width: parent.width - 30 - height: contentHeight - - ScrollBar.vertical.policy: ScrollBar.AlwaysOff - ScrollBar.horizontal.policy: ScrollBar.AlwaysOn - - ScrollBar.horizontal.visible: false - - Row { - spacing: 10 - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: true - } - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature and added longer text to check how it looks" - textVisible: true - } - - MMPhotoCard { - width: 120 - height: 120 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: false - } - } - } - - MMListSpacer { - height: __style.margin20 - } - - ScrollView { - spacing: 20 - width: parent.width - 30 - height: contentHeight - - ScrollBar.vertical.policy: ScrollBar.AlwaysOff - ScrollBar.horizontal.policy: ScrollBar.AlwaysOn - ScrollBar.horizontal.visible: false - - Row { - spacing: 10 - - MMPhotoCard { - width: 180 - height: 180 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature" - textVisible: true - } - - MMPhotoCard { - width: 180 - height: 180 - imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - text: "This is my feature and added longer text to check how it looks" - textVisible: true - } - } - } - } -} diff --git a/gallery/qml/pages/PhotosPage.qml b/gallery/qml/pages/PhotosPage.qml index 65f06f7fc..bab3c15a5 100644 --- a/gallery/qml/pages/PhotosPage.qml +++ b/gallery/qml/pages/PhotosPage.qml @@ -14,6 +14,7 @@ import QtQuick.Controls.Basic import "../../app/qml/components" import "../../app/qml/form/editors" +import "../components" as GalleryComponents Page { @@ -22,40 +23,148 @@ Page { color: __style.lightGreenColor } - Column { - width: parent.width - spacing: 20 - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: 20 - anchors.left: parent.left - anchors.leftMargin: 20 + ScrollView { + id: scrollView + anchors.fill: parent + + contentWidth: availableWidth - MMFormPhotoViewer { + Column { width: parent.width + spacing: 20 + topPadding: 20 + rightPadding: 20 + leftPadding: 20 - onCapturePhotoClicked: console.log("onCapturePhotoClicked") - onChooseFromGalleryClicked: console.log("onChooseFromGalleryClicked") - } + GalleryComponents.FormPhotoViewer { + width: parent.width - parent.leftPadding - parent.rightPadding - MMFormPhotoViewer { - width: parent.width + onCapturePhotoClicked: console.log("onCapturePhotoClicked") + onChooseFromGalleryClicked: console.log("onChooseFromGalleryClicked") + } - photoUrl: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - photoState: "valid" - } + GalleryComponents.FormPhotoViewer { + width: parent.width - parent.leftPadding - parent.rightPadding - MMFormPhotoViewer { - width: 200 + photoUrl: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + photoState: "valid" + } - photoUrl: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" - photoState: "valid" - } + GalleryComponents.FormPhotoViewer { + width: 200 + + photoUrl: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + photoState: "valid" + } + + GalleryComponents.FormPhotoViewer { + width: 200 + + photoState: "notAvailable" + } + + ScrollView { + spacing: 20 + width: parent.width - parent.leftPadding - parent.rightPadding + height: contentHeight + + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + + ScrollBar.horizontal.visible: false + + Row { + spacing: 10 + + MMPhotoCard { + size: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } + + MMPhotoCard { + size: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature and added longer text to check how it looks" + textVisible: true + } + + MMPhotoCard { + size: 120 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: false + } + } + } + + MMListSpacer { + height: __style.margin20 + } + + ScrollView { + spacing: 20 + width: parent.width - parent.leftPadding - parent.rightPadding + height: contentHeight + + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + ScrollBar.horizontal.visible: false + + Row { + spacing: 10 + + MMPhotoCard { + size: 180 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } + + MMPhotoCard { + size: 180 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature and added longer text to check how it looks" + textVisible: true + } + } + } + + MMListSpacer { + height: __style.margin20 + } + + ScrollView { + spacing: 20 + width: parent.width - parent.leftPadding - parent.rightPadding + + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + ScrollBar.horizontal.visible: false + + Row { + spacing: 10 + + MMPhotoCard { + size: (scrollView.width - scrollView.leftPadding - scrollView.rightPadding) / 2 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature" + textVisible: true + } - MMFormPhotoViewer { - width: 200 + MMPhotoCard { + size: (scrollView.width - scrollView.leftPadding - scrollView.rightPadding) / 2 + imageSource: "https://images.pexels.com/photos/615348/forest-fog-sunny-nature-615348.jpeg" + text: "This is my feature and added longer text to check how it looks,and again this my feature and added longer text to check how it looks and again is my feature and added longer text to check how it looks" + textVisible: true + } + } + } - photoState: "notAvailable" + MMListSpacer { + height: __style.margin20 + } } } } From fadc750ce2dabb612b48e825dbf8217b5bb194a2 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 23 Feb 2026 14:12:33 +0200 Subject: [PATCH 5/9] Replaced opacityMask and fastblur with MultiEffects in MMPhoto and MMPhotoCard components --- app/qml/components/MMPhoto.qml | 29 ++++++++++++--------- app/qml/components/MMPhotoCard.qml | 42 +++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/app/qml/components/MMPhoto.qml b/app/qml/components/MMPhoto.qml index c3857a41a..4ea08dc21 100644 --- a/app/qml/components/MMPhoto.qml +++ b/app/qml/components/MMPhoto.qml @@ -9,7 +9,7 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import "../components" as MMComponents import "." @@ -27,21 +27,24 @@ Image { asynchronous: true autoTransform: true layer.enabled: true - layer { - effect: OpacityMask { - maskSource: Item { - width: root.width - height: root.height - Rectangle { - anchors.centerIn: parent - width: parent.width - height: parent.height - radius: 20 * __dp - } - } + layer.effect: Component { + MultiEffect { + maskEnabled: true + maskSource: maskRect + autoPaddingEnabled: false } } + // The mask shape + Rectangle { + id: maskRect + width: root.width + height: root.height + radius: 20 * __dp + visible: false + layer.enabled: true + } + Rectangle { anchors.fill: parent color: __style.polarColor diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index 5e15f58f7..082d9422d 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -8,7 +8,7 @@ ***************************************************************************/ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects Item { id: root @@ -23,18 +23,25 @@ Item { height: size width: size + // mask for the entire card, containing both image and footer for rounded corners Rectangle { - id: maskRect - anchors.fill: parent + id: cardMask + width: root.width + height: root.height radius: __style.margin20 visible: false + layer.enabled: true } Item { anchors.fill: parent layer.enabled: true - layer.effect: OpacityMask { - maskSource: maskRect + layer.effect: Component { + MultiEffect { + maskEnabled: true + maskSource: cardMask + autoPaddingEnabled: false // keeping the geometry locked + } } MMPhoto { @@ -47,7 +54,7 @@ Item { // the footer of the card // contains the text to display over the blurred part of the image - FastBlur { + Item { id: footer visible: root.textVisible @@ -58,15 +65,24 @@ Item { } height: parent.height * 0.33 - radius: 20 - source: ShaderEffectSource { - sourceItem: bngImage - sourceRect: Qt.rect(0, bngImage.height - footer.height, bngImage.width, - footer.height) - recursive: false + clip: true + + // frosted glass effect + MultiEffect { + source: bngImage + width: bngImage.width + height: bngImage.height + + // align the blur at the bottom over the original image + y: -footer.y + + blurEnabled: true + blur: 1.0 + blurMax: 32 + autoPaddingEnabled: false } - // Tint Overlay (Child of FastBlur) + // tint overlay Rectangle { anchors.fill: parent color: __style.nightColor From 1bafbbc74c5246d6422c4a7c22f761a71119e770 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 23 Feb 2026 14:21:26 +0200 Subject: [PATCH 6/9] Removed photo card page from gallery --- gallery/qml/Main.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gallery/qml/Main.qml b/gallery/qml/Main.qml index 70eb76f6b..854b5d21b 100644 --- a/gallery/qml/Main.qml +++ b/gallery/qml/Main.qml @@ -229,10 +229,6 @@ ApplicationWindow { title: "Photos" source: "PhotosPage.qml" } - ListElement { - title: "Photo cards page" - source: "PhotoCardPage.qml" - } ListElement { title: "Map" source: "MapPage.qml" From 72c124f11c4dc136a3413286e48876ec166aff6d Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 24 Feb 2026 09:18:15 +0200 Subject: [PATCH 7/9] Simplified photo card component Added review findings --- app/qml/components/MMPhoto.qml | 16 ++-- app/qml/components/MMPhotoCard.qml | 136 +++++++++++++---------------- 2 files changed, 67 insertions(+), 85 deletions(-) diff --git a/app/qml/components/MMPhoto.qml b/app/qml/components/MMPhoto.qml index 4ea08dc21..60e41e24b 100644 --- a/app/qml/components/MMPhoto.qml +++ b/app/qml/components/MMPhoto.qml @@ -8,19 +8,15 @@ ***************************************************************************/ import QtQuick -import QtQuick.Controls import QtQuick.Effects -import "../components" as MMComponents -import "." - Image { id: root property url photoUrl property bool isLocalFile: true - signal clicked( var path ) + signal clicked( url path ) height: width source: root.photoUrl @@ -46,10 +42,10 @@ Image { } Rectangle { - anchors.fill: parent + anchors.fill: root color: __style.polarColor z: -1 - visible: root.photoUrl == '' || root.status === Image.Error // if image has transparent background, we would still see it + visible: root.photoUrl.toString() === "" || root.status === Image.Error // if image has transparent background, we would still see it MMIcon { anchors.centerIn: parent @@ -60,13 +56,13 @@ Image { } MMSingleClickMouseArea { - anchors.fill: parent + anchors.fill: root onSingleClicked: root.clicked(root.photoUrl) } - MMComponents.MMBusyIndicator { + MMBusyIndicator { id: busyIndicator - anchors.centerIn: parent + anchors.centerIn: root visible: root.status === Image.Loading } diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index 082d9422d..b5fbf20b0 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -7,7 +7,6 @@ * * ***************************************************************************/ import QtQuick -import QtQuick.Controls import QtQuick.Effects Item { @@ -18,96 +17,83 @@ Item { property bool textVisible: true property int size: 120 - signal clicked(string path) + signal clicked(url path) height: size width: size - // mask for the entire card, containing both image and footer for rounded corners + layer.enabled: true + layer.effect: MultiEffect { + maskEnabled: true + maskSource: photoMask + autoPaddingEnabled: false + } + Rectangle { - id: cardMask + id: photoMask width: root.width height: root.height - radius: __style.margin20 + radius: 20 * __dp visible: false layer.enabled: true } - Item { + MMPhoto { + id: bngImage anchors.fill: parent - layer.enabled: true - layer.effect: Component { - MultiEffect { - maskEnabled: true - maskSource: cardMask - autoPaddingEnabled: false // keeping the geometry locked - } - } + fillMode: Image.PreserveAspectCrop + autoTransform: true + smooth: true + } - MMPhoto { - id: bngImage - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - autoTransform: true - smooth: true + MultiEffect{ + id: blurFooter + height: parent.height * 0.33 + visible: root.textVisible + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom } - // the footer of the card - // contains the text to display over the blurred part of the image - Item { - id: footer - visible: root.textVisible - - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: parent.height * 0.33 - - clip: true - - // frosted glass effect - MultiEffect { - source: bngImage - width: bngImage.width - height: bngImage.height - - // align the blur at the bottom over the original image - y: -footer.y - - blurEnabled: true - blur: 1.0 - blurMax: 32 - autoPaddingEnabled: false - } - - // tint overlay - Rectangle { - anchors.fill: parent - color: __style.nightColor - opacity: 0.35 - } - - Text { - text: root.text - width: root.width - 2 * (root.size * 0.15) - anchors.centerIn: parent - - color: __style.polarColor - font: __style.t5 - - lineHeightMode: Text.FixedHeight - lineHeight: __style.margin16 - maximumLineCount: 2 - - horizontalAlignment: Text.AlignHCenter | Text.AlignJustify - verticalAlignment: Text.AlignVCenter - - wrapMode: Text.WordWrap - elide: Text.ElideRight - } + source: ShaderEffectSource { + sourceItem: bngImage + sourceRect: Qt.rect(0, bngImage.height - bngImage.height * 0.33, bngImage.width, + bngImage.height * 0.33) + recursive: false } + + autoPaddingEnabled: false + blurEnabled: true + blur: 0.8 + } + + // tint overlay + Rectangle { + anchors.fill: blurFooter + color: __style.nightColor + opacity: 0.35 + visible: root.textVisible + } + + Text { + visible: root.textVisible + text: root.text + width: root.width - 2 * (root.size * 0.15) + anchors.centerIn: blurFooter + + color: __style.polarColor + font: __style.t5 + + lineHeightMode: Text.FixedHeight + lineHeight: __style.margin16 + maximumLineCount: 2 + + horizontalAlignment: Text.AlignHCenter | Text.AlignJustify + verticalAlignment: Text.AlignVCenter + + wrapMode: Text.WordWrap + elide: Text.ElideRight } MMSingleClickMouseArea { From 0f13b283f4a08200df17f3e6d6e12550f95ad40e Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 24 Feb 2026 17:34:20 +0200 Subject: [PATCH 8/9] Implemented code findings --- app/qml/components/MMPhoto.qml | 11 +++++------ app/qml/components/MMPhotoCard.qml | 18 +++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/qml/components/MMPhoto.qml b/app/qml/components/MMPhoto.qml index 60e41e24b..68b2f31f8 100644 --- a/app/qml/components/MMPhoto.qml +++ b/app/qml/components/MMPhoto.qml @@ -6,6 +6,7 @@ * (at your option) any later version. * * * ***************************************************************************/ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Effects @@ -23,12 +24,10 @@ Image { asynchronous: true autoTransform: true layer.enabled: true - layer.effect: Component { - MultiEffect { - maskEnabled: true - maskSource: maskRect - autoPaddingEnabled: false - } + layer.effect: MultiEffect { + maskEnabled: true + maskSource: maskRect + autoPaddingEnabled: false } // The mask shape diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index b5fbf20b0..527ced9bc 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -6,6 +6,8 @@ * (at your option) any later version. * * * ***************************************************************************/ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Effects @@ -15,7 +17,7 @@ Item { property alias imageSource: bngImage.photoUrl property string text: "" property bool textVisible: true - property int size: 120 + property int size: 120 * __dp signal clicked(url path) @@ -40,20 +42,18 @@ Item { MMPhoto { id: bngImage - anchors.fill: parent + anchors.fill: root fillMode: Image.PreserveAspectCrop - autoTransform: true - smooth: true } MultiEffect{ id: blurFooter - height: parent.height * 0.33 + height: root.height * 0.33 visible: root.textVisible anchors { - left: parent.left - right: parent.right - bottom: parent.bottom + left: root.left + right: root.right + bottom: root.bottom } source: ShaderEffectSource { @@ -97,7 +97,7 @@ Item { } MMSingleClickMouseArea { - anchors.fill: parent + anchors.fill: root onSingleClicked: root.clicked(root.imageSource) } } From 21e77d666cd9cdc70d8f9fd6d5c13cd8d19c5482 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 2 Mar 2026 10:18:34 +0200 Subject: [PATCH 9/9] Set default text visible to false for MMPhotoCard --- app/qml/components/MMPhotoCard.qml | 2 +- app/qml/form/editors/MMFormGalleryEditor.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/qml/components/MMPhotoCard.qml b/app/qml/components/MMPhotoCard.qml index 527ced9bc..d92f45ae9 100644 --- a/app/qml/components/MMPhotoCard.qml +++ b/app/qml/components/MMPhotoCard.qml @@ -16,7 +16,7 @@ Item { property alias imageSource: bngImage.photoUrl property string text: "" - property bool textVisible: true + property bool textVisible: false property int size: 120 * __dp signal clicked(url path) diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index d3a519d24..3998a90ec 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -60,7 +60,7 @@ MMPrivateComponents.MMBaseInput { return '' } - textVisible: true + textVisible: false text: model.FeatureTitle