Skip to content
Snippets Groups Projects
PhotoboothView.qml 7.62 KiB
Newer Older
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls.Styles 1.4
import Qt.labs.platform 1.1
import QtGraphicalEffects 1.0
import net.jami.Models 1.0

ColumnLayout{
    property bool takePhotoState: false
    property bool hasAvatar: false
    property bool isDefaultIcon: false
    property string imgBase64: ""
    property string fileName: ""

    property int boothWidth: 224

    signal imageAcquired
    signal imageCleared

    function startBooth(force = false){
        hasAvatar = false
        ClientWrapper.accountAdaptor.startPreviewing(force)
        takePhotoState = true
    }

    function stopBooth(){
        try{
            if(!ClientWrapper.accountAdaptor.hasVideoCall()) {
                ClientWrapper.accountAdaptor.stopPreviewing()
            }
        } catch(erro){console.log("Exception: " +  erro.message)}

        takePhotoState = false
    }

    function setAvatarPixmap(avatarPixmapBase64, defaultValue = false){
        imgBase64 = avatarPixmapBase64
        stopBooth()
        if(defaultValue){
            isDefaultIcon = defaultValue
        }
    }

    onVisibleChanged: {
        if(!visible){
            stopBooth()
        }
    }

    JamiFileDialog{
        id: importFromFileToAvatar_Dialog

        mode: JamiFileDialog.OpenFile
        title: qsTr("Choose an image to be the avatar")
        folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)

        nameFilters: [ qsTr("Image Files") + " (*.png *.jpg *.jpeg)",qsTr(
                "All files") + " (*)"]

        onAccepted: {
            fileName = file
            if(fileName.length === 0) {
                imageCleared()
                return
            }
            imgBase64 = ClientWrapper.utilsAdaptor.getCroppedImageBase64FromFile(
                        ClientWrapper.utilsAdaptor.getAbsPath(fileName),boothWidth)
            imageAcquired()
            stopBooth()
        }
    }

    spacing: 0

    Layout.maximumWidth: boothWidth
    Layout.preferredWidth: boothWidth
    Layout.minimumWidth: boothWidth

    Layout.maximumHeight: 0

    Layout.alignment: Qt.AlignHCenter

    Label{
        id: avatarLabel

        visible: !takePhotoState

        Layout.maximumWidth: boothWidth
        Layout.preferredWidth: boothWidth
        Layout.minimumWidth: boothWidth
        Layout.maximumHeight: boothWidth
        Layout.preferredHeight: boothWidth
        Layout.minimumHeight: boothWidth

        Layout.alignment: Qt.AlignHCenter

        background: Rectangle {
            id: avatarLabelBackground

            anchors.fill: parent
Sébastien Blin's avatar
Sébastien Blin committed
            color: "grey"
            radius: height / 2

            Image{
                id: avatarImg

                anchors.fill: parent
Sébastien Blin's avatar
Sébastien Blin committed
                source:  {
                    if(imgBase64.length === 0){
Sébastien Blin's avatar
Sébastien Blin committed
                        return "qrc:/images/default_avatar_overlay.svg"
                    } else {
                        return "data:image/png;base64," + imgBase64
                    }
                }
                fillMode: Image.PreserveAspectCrop
                layer.enabled: true
                layer.effect: OpacityMask {
                    maskSource: Rectangle{
                        width: avatarImg.width
                        height: avatarImg.height
                        radius: {
                            var size = ((avatarImg.width <= avatarImg.height)? avatarImg.width:avatarImg.height)
                            return size /2
                        }
                    }
                }
            }
        }
    }

    PhotoboothPreviewRender{
        id:previewWidget

        onHideBooth:{
            stopBooth()
        }
        visible: takePhotoState
        focus: visible

        Layout.alignment: Qt.AlignHCenter
        Layout.maximumWidth: boothWidth
        Layout.preferredWidth: boothWidth
        Layout.minimumWidth: boothWidth
        Layout.maximumHeight: boothWidth
        Layout.preferredHeight: boothWidth
        Layout.minimumHeight: boothWidth

        layer.enabled: true
        layer.effect: OpacityMask {
            maskSource: Rectangle{
                width: previewWidget.width
                height: previewWidget.height
                radius: {
                    var size = ((previewWidget.width <= previewWidget.height)? previewWidget.width:previewWidget.height)
                    return size /2
                }
            }
        }

        Label{
            id: flashOverlay

            anchors.fill: previewWidget
            visible: false
            color: "#fff"

            OpacityAnimator on opacity{
                id: flashAnimation

                from: 1
                to: 0
                duration: 600
            }
        }
    }


    RowLayout{
        Layout.fillWidth: true
        Layout.minimumHeight: 30
        Layout.maximumHeight: 30

        Item{
            Layout.fillWidth: true
            Layout.fillHeight: true
        }

        HoverableButton {
            id: takePhotoButton

            property string cameraAltIconUrl: "qrc:/images/icons/baseline-camera_alt-24px.svg"
            property string addPhotoIconUrl: "qrc:/images/icons/round-add_a_photo-24px.svg"
            property string refreshIconUrl: "qrc:/images/icons/baseline-refresh-24px.svg"

            Layout.maximumWidth: 30
            Layout.preferredWidth: 30
            Layout.minimumWidth: 30

            Layout.minimumHeight: 30
            Layout.preferredHeight: 30
            Layout.maximumHeight: 30

            text: ""
            font.pointSize: 10
            font.kerning: true

            toolTipText: qsTr("Press this button to take photo")

            radius: height / 6
            source: {

                if(takePhotoState) {
                    toolTipText = qsTr("Press this button to finish taking photo")
                    return cameraAltIconUrl
                }

                if(hasAvatar){
                    toolTipText = qsTr("Press this button to retake photo")
                    return refreshIconUrl
                } else {
                    toolTipText = qsTr("Press this button to take photo")
                    return addPhotoIconUrl
                }
            }
            onClicked: {
                if(!takePhotoState){
                    imageCleared()
                    startBooth()
                    return
                } else {
                    // show flash overlay
                    flashOverlay.visible = true
                    flashAnimation.restart()

                    // run concurrent function call to take photo
                    imgBase64 = previewWidget.takeCroppedPhotoToBase64(boothWidth)
                    hasAvatar = true
                    imageAcquired()
                    stopBooth()
                }
            }
        }

        Item{
            Layout.fillHeight: true

            Layout.minimumWidth: 6
            Layout.preferredWidth: 6
            Layout.maximumWidth: 6
        }

        HoverableButton {
            id: importButton

            Layout.maximumWidth: 30
            Layout.preferredWidth: 30
            Layout.minimumWidth: 30

            Layout.minimumHeight: 30
            Layout.preferredHeight: 30
            Layout.maximumHeight: 30

            text: ""
            font.pointSize: 10
            font.kerning: true

            radius: height / 6
            source: "qrc:/images/icons/round-folder-24px.svg"

            toolTipText: qsTr("Import avatar from image file")

            onClicked: {
                importFromFileToAvatar_Dialog.open()
            }
        }

        Item{
            Layout.fillWidth: true
            Layout.fillHeight: true
        }
    }
}