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/MMPhoto.qml b/app/qml/components/MMPhoto.qml
index c3857a41a..68b2f31f8 100644
--- a/app/qml/components/MMPhoto.qml
+++ b/app/qml/components/MMPhoto.qml
@@ -6,13 +6,10 @@
* (at your option) any later version. *
* *
***************************************************************************/
+pragma ComponentBehavior: Bound
import QtQuick
-import QtQuick.Controls
-import Qt5Compat.GraphicalEffects
-
-import "../components" as MMComponents
-import "."
+import QtQuick.Effects
Image {
id: root
@@ -20,33 +17,34 @@ Image {
property url photoUrl
property bool isLocalFile: true
- signal clicked( var path )
+ signal clicked( url path )
height: width
source: root.photoUrl
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: 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
+ 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
@@ -57,13 +55,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
new file mode 100644
index 000000000..d92f45ae9
--- /dev/null
+++ b/app/qml/components/MMPhotoCard.qml
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: root
+
+ property alias imageSource: bngImage.photoUrl
+ property string text: ""
+ property bool textVisible: false
+ property int size: 120 * __dp
+
+ signal clicked(url path)
+
+ height: size
+ width: size
+
+ layer.enabled: true
+ layer.effect: MultiEffect {
+ maskEnabled: true
+ maskSource: photoMask
+ autoPaddingEnabled: false
+ }
+
+ Rectangle {
+ id: photoMask
+ width: root.width
+ height: root.height
+ radius: 20 * __dp
+ visible: false
+ layer.enabled: true
+ }
+
+ MMPhoto {
+ id: bngImage
+ anchors.fill: root
+ fillMode: Image.PreserveAspectCrop
+ }
+
+ MultiEffect{
+ id: blurFooter
+ height: root.height * 0.33
+ visible: root.textVisible
+ anchors {
+ left: root.left
+ right: root.right
+ bottom: root.bottom
+ }
+
+ 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 {
+ anchors.fill: root
+ onSingleClicked: root.clicked(root.imageSource)
+ }
+}
diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml
index bfa98db84..5fa27803a 100644
--- a/app/qml/form/editors/MMFormGalleryEditor.qml
+++ b/app/qml/form/editors/MMFormGalleryEditor.qml
@@ -48,12 +48,10 @@ MMPrivateComponents.MMBaseInput {
homePath: root._fieldActiveProject.homePath
}
- delegate: MMComponents.MMPhoto {
- width: rowView.height
+ delegate: MMComponents.MMPhotoCard{
+ size: rowView.height
- fillMode: Image.PreserveAspectCrop
-
- photoUrl: {
+ imageSource: {
let absolutePath = model.PhotoPath
if ( absolutePath !== '' && __inputUtils.fileExists( absolutePath ) ) {
@@ -62,6 +60,10 @@ MMPrivateComponents.MMBaseInput {
return ''
}
+ textVisible: false
+
+ text: model.FeatureTitle
+
onClicked: function( path ) {
root.openLinkedFeature( model.FeaturePair )
}
diff --git a/gallery/qml.qrc b/gallery/qml.qrc
index 9d82ad2d8..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
@@ -106,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/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
+ }
}
}
}