Commit 68e8ad46 authored by Sébastien Blin's avatar Sébastien Blin
Browse files

photoboothview: implement new design

Implement new design. Now the photobooth got a modal for
preview. This uses the RecordBox item which is simplified
into a popup instead weird Pathes

Change-Id: Id95c90f853870605ecf21c33217381092ca1a709
parent 303639fe
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g id="Icones_Outline">
<g id="Camera_Black_24dp">
<g id="Ico_Camera" transform="translate(2.000000, 4.000000)">
<g id="Fill-1">
<path d="M1.6,16.3c-0.4,0-0.8-0.2-1.1-0.5c-0.2-0.2-0.4-0.6-0.4-0.9l0-0.2l0-11C0,3.4,0.2,3,0.5,2.7C0.7,2.4,1,2.3,1.3,2.2
l0.2,0l4.4,0l1.2-2.5h5.7l1.2,2.5h4.3c0.4,0,0.8,0.2,1.1,0.5c0.2,0.2,0.4,0.6,0.4,0.9l0,0.2l0,11c0,0.4-0.2,0.8-0.5,1.1
c-0.2,0.2-0.6,0.4-0.9,0.4l-0.2,0L1.6,16.3z M1.6,3.7L1.6,3.7l-0.1,11l0,0.1l17,0l0.1,0l0-11l0-0.1l-5.3,0L12,1.2H8L6.8,3.7H1.6
z M10,13.4c-1.2,0-2.3-0.5-3.2-1.3c-0.8-0.8-1.3-2-1.3-3.2c0-1.2,0.5-2.3,1.3-3.2c0.8-0.8,2-1.3,3.2-1.3c1.2,0,2.3,0.5,3.2,1.3
c0.8,0.8,1.3,2,1.3,3.2s-0.5,2.3-1.3,3.2C12.3,12.9,11.2,13.4,10,13.4z M10,5.9c-0.8,0-1.6,0.3-2.1,0.9S7,8.1,7,8.9
c0,0.8,0.3,1.6,0.9,2.1c1.1,1.1,3.1,1.1,4.3,0c0.6-0.6,0.9-1.3,0.9-2.1c0-0.8-0.3-1.6-0.9-2.1C11.6,6.2,10.8,5.9,10,5.9z"/>
</g>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" fill="white"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<path d="M20.9,4.6H15V4.2C15,3,14,2,12.8,2h-1.6C10,2,9,3,9,4.2v0.3H3.1c-0.4,0-0.7,0.3-0.7,0.7s0.3,0.7,0.7,0.7h1.4l0.7,13.8
C5.2,21,6.3,22,7.6,22h8.9c1.3,0,2.4-1,2.4-2.3l0.7-13.8h1.4c0.4,0,0.7-0.3,0.7-0.7S21.3,4.6,20.9,4.6z M10.3,4.6V4.2
c0-0.5,0.4-0.8,0.8-0.8h1.6c0.5,0,0.8,0.4,0.8,0.8v0.3H10.3z M18.1,5.9l-0.7,13.7c0,0.6-0.5,1-1,1H7.6c-0.6,0-1-0.4-1-1L5.8,5.9
H18.1z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M10.59 4.59C10.21 4.21 9.7 4 9.17 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-1.41-1.41z"/></svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<path d="M20.7,8.5c-0.1-1.3-1.1-2.3-2.4-2.3h-5.6c-0.3,0-0.7-0.2-0.9-0.5l-0.5-0.8c-0.4-0.7-1.2-1.2-2-1.2H5.7
c-1.3,0-2.4,1.1-2.4,2.4l0.1,2.3c-0.4,0-0.7,0.2-1,0.5C2.2,9.2,2,9.6,2,10c0,0.1,0,0.2,0,0.3l1.1,7.9c0.2,1.2,1.2,2.1,2.4,2.1h13.1
c1.2,0,2.2-0.9,2.4-2.1l1.1-7.9C22.1,9.4,21.5,8.6,20.7,8.5z M5.7,5.1h3.6c0.4,0,0.7,0.2,0.9,0.5l0.5,0.8c0.4,0.7,1.2,1.2,2,1.2h5.6
c0.5,0,0.9,0.4,1,0.9H4.8L4.7,6.2l0,0C4.7,5.6,5.2,5.1,5.7,5.1z M19.5,18c-0.1,0.5-0.5,0.9-1,0.9H5.5c-0.5,0-0.9-0.4-1-0.9l-1.1-8
c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.1-0.1l16.9,0c0.1,0,0.2,0.1,0.2,0.2L19.5,18z"/>
</svg>
......@@ -25,30 +25,28 @@ import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../mainview/components"
Item {
id: root
property bool isPreviewing: false
property alias imageId: avatar.imageId
property bool newConversation: false
property real avatarSize
property real buttonSize: avatarSize
property bool inverted: false
signal focusOnPreviousItem
signal focusOnNextItem
width: avatarSize
height: boothLayout.height
height: Math.max(avatarSize, buttonSize)
function startBooth() {
preview.startWithId(VideoDevices.getDefaultDevice())
isPreviewing = true
recordBox.openRecorder(true)
}
function stopBooth(){
if (!AccountAdapter.hasVideoCall()) {
VideoDevices.stopDevice(preview.deviceId)
}
isPreviewing = false
recordBox.closeRecorder()
}
function focusOnNextPhotoBoothItem () {
......@@ -56,10 +54,7 @@ Item {
}
function focusOnPreviousPhotoBoothItem () {
if (isPreviewing)
clearButton.forceActiveFocus()
else
importButton.forceActiveFocus()
importButton.forceActiveFocus()
}
onVisibleChanged: {
......@@ -68,6 +63,21 @@ Item {
}
}
RecordBox {
id: recordBox
isPhoto: true
visible: false
onValidatePhoto: function(photo) {
if (!root.newConversation)
AccountAdapter.setCurrentAccountAvatarBase64(photo)
else
UtilsAdapter.setSwarmCreationImageFromString(photo, imageId)
buttonsRowLayout.backToAvatar()
}
}
JamiFileDialog {
id: importFromFileDialog
......@@ -109,228 +119,273 @@ Item {
}
}
ColumnLayout {
id: boothLayout
spacing: JamiTheme.preferredMarginSize / 2
Item {
id: imageLayer
Layout.preferredWidth: avatarSize
Layout.preferredHeight: avatarSize
Layout.alignment: Qt.AlignHCenter
Avatar {
id: avatar
anchors.fill: parent
anchors.margins: 1
visible: !preview.visible
Item {
id: imageLayer
mode: newConversation? Avatar.Mode.Conversation : Avatar.Mode.Account
fillMode: Image.PreserveAspectCrop
showPresenceIndicator: false
}
anchors.centerIn: parent
width: avatarSize
height: avatarSize
LocalVideo {
id: preview
Avatar {
id: avatar
anchors.fill: parent
anchors.margins: 1
anchors.fill: parent
anchors.margins: 1
visible: isPreviewing
mode: newConversation? Avatar.Mode.Conversation : Avatar.Mode.Account
rendererId: VideoDevices.getDefaultDevice()
fillMode: Image.PreserveAspectCrop
showPresenceIndicator: false
function takePhoto() {
return videoProvider.captureVideoFrame(videoSink)
HoverHandler {
target: parent
enabled: parent.visible
onHoveredChanged: {
overlayHighlighted.visible = hovered
}
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: avatarSize
height: avatarSize
radius: avatarSize / 2
}
TapHandler {
target: parent
enabled: parent.visible
onTapped: {
imageLayer.visible = false
buttonsRowLayout.visible = true
}
}
Rectangle {
id: flashRect
id: overlayHighlighted
visible: false
anchors.fill: parent
anchors.margins: 0
radius: avatarSize / 2
color: "white"
opacity: 0
color: Qt.rgba(0, 0, 0, 0.5)
radius: parent.height / 2
SequentialAnimation {
id: flashAnimation
opacity: visible
Behavior on opacity {
NumberAnimation {
target: flashRect; property: "opacity"
to: 1; duration: 0
from: 0
duration: JamiTheme.shortFadeDuration
}
NumberAnimation {
target: flashRect; property: "opacity"
to: 0; duration: 500
}
Image {
id: overlayImage
width: JamiTheme.smartListAvatarSize / 2
height: JamiTheme.smartListAvatarSize / 2
anchors.centerIn: parent
layer {
enabled: true
effect: ColorOverlay {
color: "white"
}
}
source: JamiResources.round_edit_24dp_svg
}
}
}
}
RowLayout {
id: buttonsRowLayout
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Layout.bottomMargin: parent.spacing
Layout.alignment: Qt.AlignHCenter
RowLayout {
id: buttonsRowLayout
visible: false
PushButton {
id: takePhotoButton
anchors.centerIn: parent
Layout.preferredHeight: childrenRect.height
spacing: 12
objectName: "takePhotoButton"
function backToAvatar() {
imageLayer.visible = true
buttonsRowLayout.visible = false
}
Layout.alignment: Qt.AlignHCenter
PushButton {
id: takePhotoButton
radius: JamiTheme.primaryRadius
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.takePhoto
source: isPreviewing ?
JamiResources.round_add_a_photo_24dp_svg :
JamiResources.baseline_camera_alt_24dp_svg
objectName: "takePhotoButton"
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
clicked()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Up) {
root.focusOnPreviousItem()
keyEvent.accepted = true
}
}
Layout.alignment: Qt.AlignHCenter
KeyNavigation.tab: {
if (clearButton.visible)
return clearButton
return importButton
height: buttonSize
width: buttonSize
radius: height / 2
border.width: 2
border.color: JamiTheme.textColor
normalColor: "transparent"
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.takePhoto
source: JamiResources.baseline_camera_alt_24dp_svg
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
clicked()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Up) {
root.focusOnPreviousItem()
keyEvent.accepted = true
}
KeyNavigation.down: KeyNavigation.tab
onClicked: {
if (isPreviewing) {
flashAnimation.start()
var photo = preview.takePhoto(avatarSize)
if (!root.newConversation)
AccountAdapter.setCurrentAccountAvatarBase64(photo)
else
UtilsAdapter.setSwarmCreationImageFromString(photo, imageId)
stopBooth()
return
}
}
startBooth()
}
KeyNavigation.tab: {
if (clearButton.visible)
return clearButton
return importButton
}
KeyNavigation.down: KeyNavigation.tab
onClicked: {
recordBox.parent = takePhotoButton
recordBox.x = Qt.binding(function() {
var buttonCenterX = takePhotoButton.x + takePhotoButton.width / 2
return buttonCenterX - recordBox.width / 2
})
recordBox.y = Qt.binding(function() {
var buttonY = takePhotoButton.y
return inverted? buttonY + takePhotoButton.height : buttonY - recordBox.height
})
startBooth()
}
}
PushButton {
id: clearButton
PushButton {
id: importButton
objectName: "photoboothViewClearButton"
objectName: "photoboothViewImportButton"
Layout.alignment: Qt.AlignHCenter
property bool focusAfterFileDialogClosed: false
visible: {
if (isPreviewing)
return true
if (!newConversation && LRCInstance.currentAccountAvatarSet)
return true
if (newConversation && UtilsAdapter.swarmCreationImage(imageId).length !== 0)
return true
return false
Layout.alignment: Qt.AlignHCenter
visible: parent.visible
height: buttonSize
width: buttonSize
radius: height / 2
border.width: 2
border.color: JamiTheme.textColor
normalColor: "transparent"
source: JamiResources.round_folder_24dp_svg
toolTipText: JamiStrings.importFromFile
imageColor: JamiTheme.textColor
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
focusAfterFileDialogClosed = true
clicked()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Down ||
keyEvent.key === Qt.Key_Tab) {
clearButton.forceActiveFocus()
keyEvent.accepted = true
}
}
radius: JamiTheme.primaryRadius
source: JamiResources.round_close_24dp_svg
toolTipText: isPreviewing ? JamiStrings.stopTakingPhoto :
JamiStrings.clearAvatar
imageColor: JamiTheme.textColor
KeyNavigation.up: takePhotoButton
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
clicked()
takePhotoButton.forceActiveFocus()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Down ||
keyEvent.key === Qt.Key_Tab) {
if (isPreviewing) {
root.focusOnNextItem()
} else
importButton.forceActiveFocus()
keyEvent.accepted = true
}
}
KeyNavigation.up: takePhotoButton
onClicked: {
stopBooth()
if (!isPreviewing) {
if (!root.newConversation)
AccountAdapter.setCurrentAccountAvatarBase64()
else
UtilsAdapter.setSwarmCreationImageFromString("", imageId)
}
}
onClicked: {
stopBooth()
buttonsRowLayout.backToAvatar()
importFromFileDialog.open()
}
}
PushButton {
id: importButton
objectName: "photoboothViewImportButton"
property bool focusAfterFileDialogClosed: false
PushButton {
id: clearButton
Layout.alignment: Qt.AlignHCenter
objectName: "photoboothViewClearButton"
visible: !isPreviewing
Layout.alignment: Qt.AlignHCenter
radius: JamiTheme.primaryRadius
source: JamiResources.round_folder_24dp_svg
toolTipText: JamiStrings.importFromFile
imageColor: JamiTheme.textColor
height: buttonSize
width: buttonSize
radius: height / 2
border.width: 2
border.color: JamiTheme.textColor
normalColor: "transparent"
source: JamiResources.ic_hangup_participant_24dp_svg
toolTipText: JamiStrings.clearAvatar
imageColor: JamiTheme.textColor
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
visible: {
if (!newConversation && LRCInstance.currentAccountAvatarSet)
return true
if (newConversation && UtilsAdapter.swarmCreationImage(imageId).length !== 0)
return true
return false
}
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
focusAfterFileDialogClosed = true
clicked()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Down ||
keyEvent.key === Qt.Key_Tab) {
root.focusOnNextItem()
keyEvent.accepted = true
}
KeyNavigation.up: importButton
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
clicked()
importButton.forceActiveFocus()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Down ||
keyEvent.key === Qt.Key_Tab) {
cancelButton.forceActiveFocus()
keyEvent.accepted = true
}
}
KeyNavigation.up: {
if (clearButton.visible)
return clearButton
return takePhotoButton
}
onClicked: {
if (!root.newConversation)
AccountAdapter.setCurrentAccountAvatarBase64()
else
UtilsAdapter.setSwarmCreationImageFromString("", imageId)
stopBooth()
buttonsRowLayout.backToAvatar()
}
}
onClicked: {
stopBooth()
importFromFileDialog.open()
PushButton {
id: cancelButton
preferredSize: 18
radius: height / 2
normalColor: "transparent"
source: JamiResources.round_close_24dp_svg
toolTipText: JamiStrings.cancel
imageColor: JamiTheme.textColor
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
Layout.leftMargin: -8
Keys.onPressed: function (keyEvent) {
if (keyEvent.key === Qt.Key_Enter ||
keyEvent.key === Qt.Key_Return) {
clicked()
takePhotoButton.forceActiveFocus()
keyEvent.accepted = true
} else if (keyEvent.key === Qt.Key_Down ||
keyEvent.key === Qt.Key_Tab) {
importButton.forceActiveFocus()
keyEvent.accepted = true
}
}
KeyNavigation.up: {
if (clearButton.visible)
return clearButton
if (importButton.visible)
return importButton
return takePhotoButton
}
onClicked: {
stopBooth()
buttonsRowLayout.backToAvatar()
}
}
}
}
......@@ -41,6 +41,7 @@ AbstractButton {
property int preferredMargin: 16
// Note the radius will default to preferredSize
property alias radius: background.radius
property alias border: background.border
// Text properties
property alias buttonText: textContent.text
......
......@@ -114,6 +114,7 @@ Rectangle {
newConversation: true
imageId: root.visible ? "temp" : ""
avatarSize: 180
buttonSize: JamiTheme.smartListAvatarSize
}
EditableLineEdit {
......@@ -123,7 +124,7 @@ Rectangle {
font.pointSize: JamiTheme.titleFontSize
horizontalAlignment: Text.AlignHCenter
horizontalAlignment: editable ? Text.AlignLeft : Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
placeholderText: JamiStrings.editTitle
......@@ -139,6 +140,9 @@ Rectangle {
font.pointSize: JamiTheme.titleFontSize
horizontalAlignment: editable ? Text.AlignLeft : Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
placeholderText: JamiStrings.editDescription
tooltipText: JamiStrings.editDescription
backgroundColor: root.color
......