linphone-desktop/linphone-app/ui/views/App/Calls/VideoConferenceFullscreen.qml
Julien Wadel 8c9e610107 - Fix crash and leaks on restart/quit.
- Reload conference list when current account change.
- Fix popup design (margins).
- Factorization of message banner (eg. copy text message)
- Factorization of NewConference dialog.
- Fix design on file preview in chat.
- Add participant shortcut in video conference.
- Remove old Linphone-Utils file/scripts.
- Fix avatar image in history view when clicking on username.
2022-05-13 17:38:43 +02:00

503 lines
14 KiB
QML

import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQml.Models 2.12
import QtGraphicalEffects 1.12
import Common 1.0
import Common.Styles 1.0
import Linphone 1.0
import DesktopTools 1.0
import LinphoneEnums 1.0
import UtilsCpp 1.0
import App.Styles 1.0
// Temp
import 'Incall.js' as Logic
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
Window {
id: window
// ---------------------------------------------------------------------------
property alias callModel: conference.callModel
property var caller
property bool hideButtons: !hideButtonsTimer.realRunning
property bool cameraIsReady : false
property bool previewIsReady : false
// ---------------------------------------------------------------------------
function exit (cb) {
DesktopTools.screenSaverStatus = true
// `exit` is called by `Incall.qml`.
// The `window` id can be null if the window was closed in this view.
if (!window) {
return
}
if(!window.close() && parent)
parent.close()
if (cb) {
cb()
}
}
// ---------------------------------------------------------------------------
onCallModelChanged: if(!callModel) window.exit()
Component.onCompleted: {
window.callModel = caller.callModel
}
// ---------------------------------------------------------------------------
Shortcut {
sequence: StandardKey.Close
onActivated: window.exit()
}
// ---------------------------------------------------------------------------
// =============================================================================
Rectangle {
id: conference
property CallModel callModel
property ConferenceModel conferenceModel: callModel && callModel.conferenceModel
property var _fullscreen: null
property bool listCallsOpened: false
signal openListCallsRequest()
// ---------------------------------------------------------------------------
anchors.fill: parent
focus: true
Keys.onEscapePressed: window.exit()
color: VideoConferenceStyle.backgroundColor
Connections {
target: callModel
onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height)
onStatusChanged: Logic.handleStatusChanged (status)
onVideoRequested: Logic.handleVideoRequested(callModel)
}
// ---------------------------------------------------------------------------
Rectangle{
anchors.fill: parent
visible: callModel.pausedByUser
color: VideoConferenceStyle.pauseArea.backgroundColor
z: 1
ColumnLayout{
anchors.fill: parent
spacing: 10
Item{
Layout.fillWidth: true
Layout.fillHeight: true
}
ActionButton{
Layout.alignment: Qt.AlignCenter
isCustom: true
colorSet: VideoConferenceStyle.pauseArea.play
backgroundRadius: width/2
onClicked: callModel.pausedByUser = !callModel.pausedByUser
}
Text{
Layout.alignment: Qt.AlignCenter
text: 'Vous êtes actuellement en dehors de la conférence.'
font.pointSize: VideoConferenceStyle.pauseArea.title.pointSize
font.weight: VideoConferenceStyle.pauseArea.title.weight
color: VideoConferenceStyle.pauseArea.title.color
}
Text{
Layout.alignment: Qt.AlignCenter
text: 'Cliquez sur le bouton "play" pour la rejoindre.'
font.pointSize: VideoConferenceStyle.pauseArea.description.pointSize
font.weight: VideoConferenceStyle.pauseArea.description.weight
color: VideoConferenceStyle.pauseArea.description.color
}
Item{
Layout.fillWidth: true
Layout.preferredHeight: 140
}
}
}
// -------------------------------------------------------------------------
// Conference info.
// -------------------------------------------------------------------------
RowLayout{
id: featuresRow
// Aux features
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 10
anchors.leftMargin: 25
anchors.rightMargin: 25
spacing: 10
visible: !window.hideButtons
height: visible? undefined : 0
/*
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.callsList
visible: !listCallsOpened && !window.hideButtons
onClicked: openListCallsRequest()
}*/
ActionButton{
id: keypadButton
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.dialpad
onClicked: telKeypad.visible = !telKeypad.visible
}
// Title
Text{
Timer{
id: elapsedTimeRefresher
running: true
interval: 1000
repeat: true
onTriggered: if(conference.conferenceModel) parent.elaspedTime = ' - ' +Utils.formatElapsedTime(conference.conferenceModel.getElapsedSeconds())
}
property string elaspedTime
horizontalAlignment: Qt.AlignHCenter
Layout.fillWidth: true
text: conference.conferenceModel ? conference.conferenceModel.subject+ elaspedTime : ''
color: VideoConferenceStyle.title.color
font.pointSize: VideoConferenceStyle.title.pointSize
}
// Mode buttons
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.screenSharing
visible: false //TODO
}
ActionButton {
id: recordingSwitch
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.record
property CallModel callModel: conference.callModel
onCallModelChanged: if(!callModel) callModel.stopRecording()
visible: SettingsModel.callRecorderEnabled && callModel
toggled: callModel.recording
onClicked: {
return !toggled
? callModel.startRecording()
: callModel.stopRecording()
}
tooltipText: !toggled? 'Start Recording' : 'Stop Recording'
}
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.screenshot
visible: conference.callModel.snapshotEnabled
onClicked: conference.callModel.takeSnapshot()
tooltipText:'take Snapshot'
}
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.stopFullscreen
onClicked: window.exit()
}
}
// -------------------------------------------------------------------------
// Contacts visual.
// -------------------------------------------------------------------------
MouseArea{
id: mainGrid
anchors.top: featuresRow.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: actionsButtons.top
anchors.topMargin: window.hideButtons ? 0 : 15
anchors.bottomMargin: window.hideButtons ? 0 : 20
onClicked: {
if(!conference.callModel)
grid.add({color: '#'+ Math.floor(Math.random()*255).toString(16)
+Math.floor(Math.random()*255).toString(16)
+Math.floor(Math.random()*255).toString(16)})
}
Component{
id: gridComponent
VideoConferenceGrid{
id: grid
Layout.leftMargin: window.hideButtons ? 15 : 70
Layout.rightMargin: rightMenu.visible ? 15 : 70
callModel: conference.callModel
onWidthChanged: console.log("Width: "+width)
}
}
Component{
id: activeSpeakerComponent
VideoConferenceActiveSpeaker{
id: activeSpeaker
callModel: conference.callModel
isRightReducedLayout: rightMenu.visible
isLeftReducedLayout: conference.listCallsOpened
}
}
RowLayout{
anchors.fill: parent
Loader{
id: conferenceLayout
Layout.fillHeight: true
Layout.fillWidth: true
sourceComponent: conference.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutGrid || !conference.callModel.videoEnabled? gridComponent : activeSpeakerComponent
onSourceComponentChanged: console.log(conference.callModel.conferenceVideoLayout)
active: conference.callModel
ColumnLayout {
anchors.fill: parent
visible: !conference.callModel || !conferenceLayout.item || conferenceLayout.item.participantCount == 0
BusyIndicator{
Layout.preferredHeight: 50
Layout.preferredWidth: 50
Layout.alignment: Qt.AlignCenter
running: parent.visible
color: VideoConferenceStyle.buzyColor
}
Text{
Layout.alignment: Qt.AlignCenter
text: "Video conference is not ready. Please Wait..."
color: VideoConferenceStyle.buzyColor
}
}
}
VideoConferenceMenu{
id: rightMenu
Layout.fillHeight: true
Layout.preferredWidth: 400
Layout.rightMargin: 30
callModel: conference.callModel
conferenceModel: conference.conferenceModel
visible: false
onClose: rightMenu.visible = !rightMenu.visible
}
}
}
// -------------------------------------------------------------------------
// Action Buttons.
// -------------------------------------------------------------------------
// Security
ActionButton{
visible: false // TODO
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
anchors.leftMargin: 25
height: VideoConferenceStyle.buttons.secure.buttonSize
width: height
isCustom: true
iconIsCustom: false
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.secure
icon: 'secure_level_1'
}
// Action buttons
RowLayout{
id: actionsButtons
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: visible ? 30 : 0
height: visible ? 60 : 0
spacing: 30
z: 2
visible: !window.hideButtons
RowLayout{
spacing: 10
Row {
spacing: 2
visible: SettingsModel.muteMicrophoneEnabled
property bool microMuted: callModel.microMuted
VuMeter {
enabled: !parent.microMuted
Timer {
interval: 50
repeat: true
running: parent.enabled
onTriggered: parent.value = callModel.microVu
}
}
ActionSwitch {
id: micro
isCustom: true
backgroundRadius: 90
colorSet: parent.microMuted ? VideoConferenceStyle.buttons.microOff : VideoConferenceStyle.buttons.microOn
onClicked: callModel.microMuted = !parent.microMuted
}
}
Row {
spacing: 2
property bool speakerMuted: callModel.speakerMuted
VuMeter {
enabled: !parent.speakerMuted
Timer {
interval: 50
repeat: true
running: parent.enabled
onTriggered: parent.value = callModel.speakerVu
}
}
ActionSwitch {
id: speaker
isCustom: true
backgroundRadius: 90
colorSet: parent.speakerMuted ? VideoConferenceStyle.buttons.speakerOff : VideoConferenceStyle.buttons.speakerOn
onClicked: callModel.speakerMuted = !parent.speakerMuted
}
}
ActionSwitch {
id: camera
isCustom: true
backgroundRadius: 90
colorSet: callModel && callModel.cameraEnabled ? VideoConferenceStyle.buttons.cameraOn : VideoConferenceStyle.buttons.cameraOff
updating: callModel.videoEnabled && callModel.updating
enabled: callModel.videoEnabled
onClicked: if(callModel) callModel.cameraEnabled = !callModel.cameraEnabled
}
}
RowLayout{
spacing: 10
ActionButton{
isCustom: true
backgroundRadius: width/2
visible: SettingsModel.callPauseEnabled
updating: callModel.updating
colorSet: callModel.pausedByUser ? VideoConferenceStyle.buttons.play : VideoConferenceStyle.buttons.pause
onClicked: callModel.pausedByUser = !callModel.pausedByUser
}
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.hangup
onClicked: callModel.terminate()
}
}
}
// Panel buttons
RowLayout{
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: visible ? 30 : 0
anchors.rightMargin: 25
height: visible ? 60 : 0
visible: !window.hideButtons
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.chat
visible: false // TODO for next version
}
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.participants
visible: false // TODO
}
ActionButton {
id: callQuality
isCustom: true
backgroundRadius: 4
colorSet: VideoConferenceStyle.buttons.callQuality
percentageDisplayed: 0
onClicked: {Logic.openCallStatistics();}
Timer {
interval: 500
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
// Note: `quality` is in the [0, 5] interval and -1.
var quality = callModel.quality
if(quality >= 0)
callQuality.percentageDisplayed = quality * 100 / 5
else
callQuality.percentageDisplayed = 0
}
}
}
ActionButton{
isCustom: true
backgroundRadius: width/2
colorSet: VideoConferenceStyle.buttons.options
onClicked: rightMenu.visible = !rightMenu.visible
}
}
// ---------------------------------------------------------------------------
// TelKeypad.
// ---------------------------------------------------------------------------
CallStatistics {
id: callStatistics
call: conference.callModel
width: conference.width - 20
height: conference.height * 2/3
relativeTo: conference
relativeY: CallStyle.header.stats.relativeY
relativeX: 10
onClosed: Logic.handleCallStatisticsClosed()
}
}
TelKeypad {
id: telKeypad
call: callModel
visible: SettingsModel.showTelKeypadAutomatically
}
MouseArea{
Timer {
id: hideButtonsTimer
property bool realRunning : true
interval: 5000
running: true
triggeredOnStart: true
onTriggered: {if(realRunning != running) realRunning = running}
function startTimer(){
restart();
}
function stopTimer(){
stop();
realRunning = false;
}
}
anchors.fill: parent
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
cursorShape: Qt.ArrowCursor
onEntered: hideButtonsTimer.startTimer()
onExited: hideButtonsTimer.stopTimer()
onPositionChanged: {
hideButtonsTimer.startTimer()
}
}
}