blob: 2f4eac56a575754d5c5c5f331b4b297f3754a130 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001
2/*
3 * Copyright (C) 2020 by Savoir-faire Linux
4 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
Sébastien Blin8940f3c2020-07-23 17:03:11 -04005 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
Sébastien Blin1f915762020-08-03 13:27:42 -04006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20import QtQuick 2.14
21import QtQuick.Controls 2.14
22import QtQuick.Layouts 1.14
23import QtQuick.Controls.Universal 2.12
24import QtQml 2.14
25import net.jami.Models 1.0
26
27import "../js/contactpickercreation.js" as ContactPickerCreation
28
29import "../../commoncomponents"
30
31Rectangle {
32 id: callOverlayRect
33
Sébastien Blin1f915762020-08-03 13:27:42 -040034 property string timeText: "00:00"
35
Sébastien Blin1f915762020-08-03 13:27:42 -040036 signal overlayChatButtonClicked
37
Sébastien Blin6607e0e2020-07-24 15:15:47 -040038 property var participantOverlays: []
39 property var participantComponent: Qt.createComponent("ParticipantOverlay.qml")
40
Sébastien Blin8940f3c2020-07-23 17:03:11 -040041 function setRecording(isRecording) {
42 callViewContextMenu.isRecording = isRecording
43 recordingRect.visible = isRecording
44 }
45
Sébastien Blin1f915762020-08-03 13:27:42 -040046 function updateButtonStatus(isPaused, isAudioOnly, isAudioMuted, isVideoMuted, isRecording, isSIP, isConferenceCall) {
Sébastien Blin8940f3c2020-07-23 17:03:11 -040047 callViewContextMenu.isSIP = isSIP
48 callViewContextMenu.isPaused = isPaused
49 callViewContextMenu.isAudioOnly = isAudioOnly
50 callViewContextMenu.isRecording = isRecording
51 recordingRect.visible = isRecording
Sébastien Blin1f915762020-08-03 13:27:42 -040052 callOverlayButtonGroup.setButtonStatus(isPaused, isAudioOnly,
53 isAudioMuted, isVideoMuted,
54 isRecording, isSIP,
55 isConferenceCall)
56 }
57
Sébastien Blin6607e0e2020-07-24 15:15:47 -040058 function updateMaster() {
59 callOverlayButtonGroup.updateMaster()
60 }
61
Sébastien Blin1f915762020-08-03 13:27:42 -040062 function showOnHoldImage(visible) {
63 onHoldImage.visible = visible
64 }
65
66 function closePotentialContactPicker() {
67 ContactPickerCreation.closeContactPicker()
68 }
69
Sébastien Blin6607e0e2020-07-24 15:15:47 -040070 function handleParticipantsInfo(infos) {
71 videoCallOverlay.updateMaster()
72 var isMaster = CallAdapter.isCurrentMaster()
73 for (var p in participantOverlays) {
74 if (participantOverlays[p])
75 participantOverlays[p].destroy()
76 }
77 participantOverlays = []
78 if (infos.length == 0) {
79 previewRenderer.visible = true
80 } else {
81 previewRenderer.visible = false
82 for (var infoVariant in infos) {
83 var hover = participantComponent.createObject(callOverlayRectMouseArea, {
84 x: distantRenderer.getXOffset() + infos[infoVariant].x * distantRenderer.getScaledWidth(),
85 y: distantRenderer.getYOffset() + infos[infoVariant].y * distantRenderer.getScaledHeight(),
86 width: infos[infoVariant].w * distantRenderer.getScaledWidth(),
87 height: infos[infoVariant].h * distantRenderer.getScaledHeight(),
88 visible: infos[infoVariant].w != 0 && infos[infoVariant].h != 0
89 })
90 if (!hover) {
91 console.log("Error when creating the hover")
92 return
93 }
94 hover.setParticipantName(infos[infoVariant].bestName)
95 hover.active = infos[infoVariant].active;
96 hover.isLocal = infos[infoVariant].isLocal;
97 hover.setMenuVisible(isMaster)
98 hover.uri = infos[infoVariant].uri
99 hover.injectedContextMenu = participantContextMenu
100 participantOverlays.push(hover)
101 }
102 }
103 }
104
Sébastien Blin1f915762020-08-03 13:27:42 -0400105 anchors.fill: parent
106
107
108 /*
109 * Timer to decide when overlay fade out.
110 */
111 Timer {
112 id: callOverlayTimer
113 interval: 5000
114 onTriggered: {
115 if (overlayUpperPartRect.state !== 'freezed') {
116 overlayUpperPartRect.state = 'freezed'
117 }
118 if (callOverlayButtonGroup.state !== 'freezed') {
119 callOverlayButtonGroup.state = 'freezed'
120 }
121 }
122 }
123
124 Rectangle {
125 id: overlayUpperPartRect
126
127 anchors.top: callOverlayRect.top
128
129 width: callOverlayRect.width
130 height: 50
131 opacity: 0
132
133 RowLayout {
134 id: overlayUpperPartRectRowLayout
135
136 anchors.fill: parent
137
Sébastien Blin1f915762020-08-03 13:27:42 -0400138 Text {
139 id: jamiBestNameText
140
141 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
142 Layout.preferredWidth: overlayUpperPartRect.width / 3
143 Layout.preferredHeight: 50
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400144 leftPadding: 16
Sébastien Blin1f915762020-08-03 13:27:42 -0400145
146 font.pointSize: JamiTheme.textFontSize
147
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400148 horizontalAlignment: Text.AlignLeft
Sébastien Blin1f915762020-08-03 13:27:42 -0400149 verticalAlignment: Text.AlignVCenter
150
151 text: textMetricsjamiBestNameText.elidedText
152 color: "white"
153
154 TextMetrics {
155 id: textMetricsjamiBestNameText
156 font: jamiBestNameText.font
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400157 text: videoCallPageRect.bestName
Sébastien Blin1f915762020-08-03 13:27:42 -0400158 elideWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400159 elide: Qt.ElideRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400160 }
161 }
162
163 Text {
164 id: callTimerText
Sébastien Blin1f915762020-08-03 13:27:42 -0400165 Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
166 Layout.preferredWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400167 Layout.preferredHeight: 48
Sébastien Blin1f915762020-08-03 13:27:42 -0400168 font.pointSize: JamiTheme.textFontSize
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400169 horizontalAlignment: Text.AlignRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400170 verticalAlignment: Text.AlignVCenter
Sébastien Blin1f915762020-08-03 13:27:42 -0400171 text: textMetricscallTimerText.elidedText
172 color: "white"
Sébastien Blin1f915762020-08-03 13:27:42 -0400173 TextMetrics {
174 id: textMetricscallTimerText
175 font: callTimerText.font
176 text: timeText
177 elideWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400178 elide: Qt.ElideRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400179 }
180 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400181
182 Rectangle {
183 id: recordingRect
184 Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
185 height: 16
186 width: 16
187 radius: height / 2
188 color: "red"
189
190 SequentialAnimation on color {
191 loops: Animation.Infinite
192 running: true
193 ColorAnimation { from: "red"; to: "transparent"; duration: 500 }
194 ColorAnimation { from: "transparent"; to: "red"; duration: 500 }
195 }
196 }
197
198 Item {
199 width: 8
200 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400201 }
202
203 color: "transparent"
204
205
206 /*
207 * Rect states: "entered" state should make overlay fade in,
208 * "freezed" state should make overlay fade out.
209 * Combine with PropertyAnimation of opacity.
210 */
211 states: [
212 State {
213 name: "entered"
214 PropertyChanges {
215 target: overlayUpperPartRect
216 opacity: 1
217 }
218 },
219 State {
220 name: "freezed"
221 PropertyChanges {
222 target: overlayUpperPartRect
223 opacity: 0
224 }
225 }
226 ]
227
228 transitions: Transition {
229 PropertyAnimation {
230 target: overlayUpperPartRect
231 property: "opacity"
232 duration: 1000
233 }
234 }
235 }
236
237 Image {
238 id: onHoldImage
239
240 anchors.verticalCenter: callOverlayRect.verticalCenter
241 anchors.horizontalCenter: callOverlayRect.horizontalCenter
242
243 width: 200
244 height: 200
245
246 visible: false
247
248 fillMode: Image.PreserveAspectFit
249 source: "qrc:/images/icons/ic_pause_white_100px.png"
250 asynchronous: true
251 }
252
253 CallOverlayButtonGroup {
254 id: callOverlayButtonGroup
255
256 anchors.bottom: callOverlayRect.bottom
257 anchors.bottomMargin: 10
258 anchors.horizontalCenter: callOverlayRect.horizontalCenter
259
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400260 height: 56
261 width: callOverlayRect.width
Sébastien Blin1f915762020-08-03 13:27:42 -0400262 opacity: 0
263
264 onChatButtonClicked: {
265 callOverlayRect.overlayChatButtonClicked()
266 }
267
268 onAddToConferenceButtonClicked: {
Sébastien Blin1f915762020-08-03 13:27:42 -0400269 /*
270 * Create contact picker - conference.
271 */
272 ContactPickerCreation.createContactPickerObjects(
273 ContactPicker.ContactPickerType.JAMICONFERENCE,
274 callOverlayRect)
275 ContactPickerCreation.calculateCurrentGeo(
276 callOverlayRect.width / 2, callOverlayRect.height / 2)
277 ContactPickerCreation.openContactPicker()
278 }
279
Sébastien Blin1f915762020-08-03 13:27:42 -0400280 states: [
281 State {
282 name: "entered"
283 PropertyChanges {
284 target: callOverlayButtonGroup
285 opacity: 1
286 }
287 },
288 State {
289 name: "freezed"
290 PropertyChanges {
291 target: callOverlayButtonGroup
292 opacity: 0
293 }
294 }
295 ]
296
297 transitions: Transition {
298 PropertyAnimation {
299 target: callOverlayButtonGroup
300 property: "opacity"
301 duration: 1000
302 }
303 }
304 }
305
306
307 /*
308 * MouseAreas to make sure that overlay states are correctly set.
309 */
310 MouseArea {
311 id: callOverlayButtonGroupLeftSideMouseArea
312
313 anchors.bottom: callOverlayRect.bottom
314 anchors.left: callOverlayRect.left
315
316 width: callOverlayRect.width / 6
317 height: 60
318
319 hoverEnabled: true
320 propagateComposedEvents: true
321 acceptedButtons: Qt.NoButton
322
323 onEntered: {
324 callOverlayRectMouseArea.entered()
325 }
326
327 onMouseXChanged: {
328 callOverlayRectMouseArea.entered()
329 }
330 }
331
332 MouseArea {
333 id: callOverlayButtonGroupRightSideMouseArea
334
335 anchors.bottom: callOverlayRect.bottom
336 anchors.right: callOverlayRect.right
337
338 width: callOverlayRect.width / 6
339 height: 60
340
341 hoverEnabled: true
342 propagateComposedEvents: true
343 acceptedButtons: Qt.NoButton
344
345 onEntered: {
346 callOverlayRectMouseArea.entered()
347 }
348
349 onMouseXChanged: {
350 callOverlayRectMouseArea.entered()
351 }
352 }
353
354 MouseArea {
355 id: callOverlayRectMouseArea
356
357 anchors.top: callOverlayRect.top
Sébastien Blin1f915762020-08-03 13:27:42 -0400358
359 width: callOverlayRect.width
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400360 height: callOverlayRect.height
Sébastien Blin1f915762020-08-03 13:27:42 -0400361
362 hoverEnabled: true
363 propagateComposedEvents: true
364 acceptedButtons: Qt.NoButton
365
366 onEntered: {
367 if (overlayUpperPartRect.state !== 'entered') {
368 overlayUpperPartRect.state = 'entered'
369 }
370 if (callOverlayButtonGroup.state !== 'entered') {
371 callOverlayButtonGroup.state = 'entered'
372 }
373 callOverlayTimer.restart()
374 }
375
376 onMouseXChanged: {
377 if (overlayUpperPartRect.state !== 'entered') {
378 overlayUpperPartRect.state = 'entered'
379 }
380 if (callOverlayButtonGroup.state !== 'entered') {
381 callOverlayButtonGroup.state = 'entered'
382 }
383 callOverlayTimer.restart()
384 }
385 }
386
387 color: "transparent"
388
Sébastien Blin1f915762020-08-03 13:27:42 -0400389 onWidthChanged: {
390 ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
391 callOverlayRect.height / 2)
392 }
393
394 onHeightChanged: {
395 ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
396 callOverlayRect.height / 2)
397 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400398
399 CallViewContextMenu {
400 id: callViewContextMenu
401
402 onTransferCallButtonClicked: {
403 /*
404 * Create contact picker - sip transfer.
405 */
406 ContactPickerCreation.createContactPickerObjects(
407 ContactPicker.ContactPickerType.SIPTRANSFER,
408 callOverlayRect)
409 ContactPickerCreation.calculateCurrentGeo(
410 callOverlayRect.width / 2, callOverlayRect.height / 2)
411 ContactPickerCreation.openContactPicker()
412 }
413 }
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400414
415 ParticipantContextMenu {
416 id: participantContextMenu
417 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400418}