feat(shared/EditCroppedImagePanel): Generic modal to support image cropping

To be used by

- User image
- Community logo
- Community banner

Updates: #5118
This commit is contained in:
Stefan 2022-05-04 11:25:01 +02:00 committed by Stefan Dunca
parent 12cb76699c
commit 31fbc47053
5 changed files with 274 additions and 2 deletions

3
.gitignore vendored
View File

@ -51,7 +51,8 @@ CTestTestfile.cmake
_deps _deps
.cache/ .cache/
# QtCreator shadow builds
build-*/
# https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore # https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
# Prerequisites # Prerequisites

@ -1 +1 @@
Subproject commit a9c79b03595d7c8f515aa014cad0471dec7a6b11 Subproject commit 457e5fe4af51a0c32a0b81b86fd2f9ebdce7d32d

View File

@ -0,0 +1,218 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.3
import utils 1.0
import shared.panels 1.0
import shared.popups 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Layout 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Popups 0.1
Item {
id: root
property alias source: bannerPreview.source
property alias cropRect: bannerPreview.cropRect
/*required*/ property alias aspectRatio: bannerPreview.aspectRatio
property bool roundedImage: true
/*required*/ property string imageFileDialogTitle: ""
/*required*/ property string title: ""
/*required*/ property string acceptButtonText: ""
property string dataImage: ""
property bool userSelectedImage: false
readonly property bool nothingToShow: state === d.noImageState
implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight
states: [
State {
name: d.dataImageState
when: root.dataImage.length > 0 && !userSelectedImage
},
State {
name: d.noImageState
when: root.dataImage.length === 0 && !userSelectedImage
},
State {
name: d.imageSelectedState
when: userSelectedImage
}
]
QtObject {
id: d
readonly property string dataImageState: "dataImage"
readonly property string noImageState: "noImage"
readonly property string imageSelectedState: "imageSelected"
}
ColumnLayout {
id: mainLayout
anchors.fill: parent
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: !bannerEditor.visible
StatusRoundedImage {
id: profilePic
anchors.fill: parent
visible: root.state === d.dataImageState
image.source: root.dataImage
showLoadingIndicator: true
border.width: 1
border.color: Style.current.border
}
StatusImageCropPanel
{
id: bannerPreview
anchors.fill: parent
visible: root.state === d.imageSelectedState
interactive: false
wallColor: Theme.palette.statusAppLayout.backgroundColor
wallTransparency: 1
margins: 0
windowStyle: roundedImage ? StatusImageCrop.WindowStyle.Rounded : StatusImageCrop.WindowStyle.Rectangular
}
StatusRoundButton {
id: editButton
icon.name: "edit"
// bottom-right corner
x: root.userSelectedImage
? bannerPreview.cropWindow.x + bannerPreview.cropWindow.width - (roundedImage ? editButton.width : editButton.width/2 + bannerPreview.radius)
: parent.width - editButton.width
y: (root.userSelectedImage
? bannerPreview.cropWindow.y + bannerPreview.cropWindow.height - (roundedImage ? editButton.height + Style.current.smallPadding : editButton.height/2)
: parent.width - editButton.height) - Style.current.smallPadding
type: StatusRoundButton.Type.Secondary
onClicked: bannerFileDialog.open()
}
}
Rectangle {
id: bannerEditor
Layout.fillWidth: true
Layout.fillHeight: true
visible: root.state === d.noImageState
radius: roundedImage ? Math.max(width, height)/2 : bannerPreview.radius
color: Style.current.inputBackground
StatusRoundButton {
id: addButton
icon.name: "add"
// top-right corner
x: parent.width - (roundedImage ? editButton.width : editButton.width/2 + bannerEditor.radius)
y: roundedImage ? addButton.height/2 : -addButton.height/2 + bannerEditor.radius
type: StatusRoundButton.Type.Secondary
onClicked: bannerFileDialog.open()
z: bannerEditor.z + 1
}
FileDialog {
id: bannerFileDialog
title: root.imageFileDialogTitle
folder: root.userSelectedImage ? bannerCropper.source.substr(0, bannerCropper.source.lastIndexOf("/")) : shortcuts.pictures
nameFilters: [qsTr("Image files (*.jpg *.jpeg, *.jfif, *.png *.tiff *.heif)")]
onAccepted: {
if(bannerFileDialog.fileUrls.length > 0) {
bannerCropper.source = bannerFileDialog.fileUrls[0]
bannerCropperModal.open()
}
}
onRejected: {
if(root.userSelectedImage)
bannerCropperModal.open()
}
}
StatusModal {
id: bannerCropperModal
header.title: root.title
anchors.centerIn: Overlay.overlay
Item {
implicitWidth: 480
implicitHeight: 350
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
StatusImageCropPanel {
id: bannerCropper
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.leftMargin: Style.current.padding * 2
Layout.topMargin: Style.current.bigPadding
Layout.rightMargin: Layout.leftMargin
Layout.bottomMargin: Layout.topMargin
windowStyle: bannerPreview.windowStyle
aspectRatio: bannerPreview.aspectRatio
enableCheckers: true
}
}
}
rightButtons: [
StatusButton {
text: root.acceptButtonText
enabled: bannerCropper.sourceSize.width > 0 && bannerCropper.sourceSize.height > 0
onClicked: {
bannerCropperModal.close()
bannerPreview.setCropRect(bannerCropper.cropRect)
bannerPreview.source = bannerCropper.source
root.userSelectedImage = true
}
}
]
}
}
}
}

View File

@ -0,0 +1,51 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import utils 1.0
import shared.panels 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
/brief Image icon and ulopad text hints for banner and logo
*/
Item {
id: root
implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight
property bool showARHint: false
ColumnLayout {
id: mainLayout
SVGImage {
id: imageImg
source: Style.svg("images_icon")
width: 20
height: 18
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
}
StatusBaseText {
id: uploadText
text: qsTr("Upload")
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.topMargin: 5
font.pixelSize: 15
color: Theme.palette.baseColor1
}
StatusBaseText {
id: optimalARText
text: qsTr("Wide aspect ratio is optimal")
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
visible: root.showARHint
Layout.topMargin: 5
font.pixelSize: 15
color: Theme.palette.baseColor1
}
}
}

View File

@ -16,3 +16,5 @@ SplitViewHandle 1.0 SplitViewHandle.qml
StyledText 1.0 StyledText.qml StyledText 1.0 StyledText.qml
SVGImage 1.0 SVGImage.qml SVGImage 1.0 SVGImage.qml
TextWithLabel 1.0 TextWithLabel.qml TextWithLabel 1.0 TextWithLabel.qml
EditCroppedImagePanel 1.0 EditCroppedImagePanel.qml
NoImageUploadedPanel 1.0 NoImageUploadedPanel.qml