2017-04-19 23:25:48 +03:00
/*
This file is part of Telegram Desktop ,
2018-01-03 13:23:14 +03:00
the official desktop application for the Telegram messaging service .
2017-04-19 23:25:48 +03:00
2018-01-03 13:23:14 +03:00
For license and copyright information please follow this link :
https : //github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2017-04-19 23:25:48 +03:00
*/
# include "calls/calls_panel.h"
2017-09-26 14:49:16 +03:00
# include "data/data_photo.h"
2018-01-17 19:21:01 +03:00
# include "data/data_session.h"
2019-01-04 15:09:48 +04:00
# include "data/data_user.h"
2019-09-16 14:14:06 +03:00
# include "data/data_file_origin.h"
2020-05-25 18:16:04 +04:00
# include "data/data_photo_media.h"
2020-05-28 18:32:10 +04:00
# include "data/data_cloud_file.h"
2020-06-12 16:12:34 +04:00
# include "data/data_changes.h"
2021-07-27 21:09:13 +03:00
# include "calls/group/calls_group_common.h"
2017-04-25 19:45:41 +03:00
# include "calls/calls_emoji_fingerprint.h"
2020-08-06 20:55:58 +04:00
# include "calls/calls_signal_bars.h"
# include "calls/calls_userpic.h"
# include "calls/calls_video_bubble.h"
2021-06-10 17:16:17 +04:00
# include "calls/calls_video_incoming.h"
2021-02-03 14:43:31 +04:00
# include "ui/platform/ui_platform_window_title.h"
2020-11-28 15:00:06 +03:00
# include "ui/widgets/call_button.h"
2017-04-19 23:25:48 +03:00
# include "ui/widgets/buttons.h"
# include "ui/widgets/labels.h"
# include "ui/widgets/shadow.h"
2021-07-28 16:24:11 +03:00
# include "ui/widgets/rp_window.h"
2021-07-27 21:09:13 +03:00
# include "ui/layers/layer_manager.h"
# include "ui/layers/generic_box.h"
2018-10-23 13:44:42 +04:00
# include "ui/image/image.h"
2020-09-30 14:32:02 +03:00
# include "ui/text/format_values.h"
2017-09-30 21:26:45 +03:00
# include "ui/wrap/fade_wrap.h"
2020-08-14 12:32:05 +04:00
# include "ui/wrap/padding_wrap.h"
2019-09-16 14:14:06 +03:00
# include "ui/platform/ui_platform_utility.h"
2021-05-19 16:59:31 +04:00
# include "ui/gl/gl_surface.h"
2021-06-10 17:16:17 +04:00
# include "ui/gl/gl_shader.h"
2020-08-21 16:34:37 +04:00
# include "ui/toast/toast.h"
2017-12-05 12:43:18 +04:00
# include "ui/empty_userpic.h"
2018-10-13 20:35:30 +03:00
# include "ui/emoji_config.h"
2022-09-17 00:23:27 +04:00
# include "ui/painter.h"
2024-03-21 21:03:21 +03:00
# include "ui/rect.h"
2019-01-21 17:42:21 +04:00
# include "core/application.h"
2017-04-13 11:27:10 +03:00
# include "lang/lang_keys.h"
2019-07-24 13:45:24 +02:00
# include "main/main_session.h"
2017-04-19 23:25:48 +03:00
# include "apiwrap.h"
# include "platform/platform_specific.h"
2024-03-01 07:24:22 +03:00
# include "base/event_filter.h"
2020-08-13 14:32:11 +04:00
# include "base/platform/base_platform_info.h"
2022-02-04 09:43:56 +03:00
# include "base/power_save_blocker.h"
2022-09-05 19:03:52 +04:00
# include "media/streaming/media_streaming_utility.h"
2017-05-12 18:27:19 +03:00
# include "window/main_window.h"
2024-01-24 12:42:37 +04:00
# include "webrtc/webrtc_environment.h"
2020-07-31 18:36:35 +04:00
# include "webrtc/webrtc_video_track.h"
2019-09-16 14:14:06 +03:00
# include "styles/style_calls.h"
2020-10-10 12:15:37 +03:00
# include "styles/style_chat.h"
2017-04-19 23:25:48 +03:00
2019-09-04 10:19:15 +03:00
# include <QtWidgets/QApplication>
2020-08-12 20:58:24 +04:00
# include <QtGui/QWindow>
2021-06-21 11:29:29 +04:00
# include <QtCore/QTimer>
2024-03-21 21:03:21 +03:00
# include <QtSvg/QSvgRenderer>
2019-09-04 10:19:15 +03:00
2017-04-19 23:25:48 +03:00
namespace Calls {
2024-03-21 21:03:21 +03:00
namespace {
[[nodiscard]] QByteArray BatterySvg (
const QSize & s ,
const QColor & c ) {
const auto color = u " rgb(%1,%2,%3) " _q
. arg ( c . red ( ) )
. arg ( c . green ( ) )
. arg ( c . blue ( ) )
. toUtf8 ( ) ;
const auto width = QString : : number ( s . width ( ) ) . toUtf8 ( ) ;
const auto height = QString : : number ( s . height ( ) ) . toUtf8 ( ) ;
return R " (
< svg width = " ) " + width + R " ( " height = " ) " + height
+ R " ( " viewBox = " 0 0 ) " + width + R " ( ) " + height + R " ( " fill = " none " >
< rect x = " 1.33598 " y = " 0.5 " width = " 24 " height = " 12 " rx = " 4 " stroke = " ) " + color + R " ( " / >
< path
d = " M26.836 4.66666V8.66666C27.6407 8.32788 28.164 7.53979 28.164 6.66666C28.164 5.79352 27.6407 5.00543 26.836 4.66666Z "
fill = " ) " + color + R " ( " / >
< path
d = " M 5.5 3.5 H 5.5 A 0.5 0.5 0 0 1 6 4 V 9 A 0.5 0.5 0 0 1 5.5 9.5 H 5.5 A 0.5 0.5 0 0 1 5 9 V 4 A 0.5 0.5 0 0 1 5.5 3.5 Z M 5 4 V 9 A 0.5 0.5 0 0 0 5.5 9.5 H 5.5 A 0.5 0.5 0 0 0 6 9 V 4 A 0.5 0.5 0 0 0 5.5 3.5 H 5.5 A 0.5 0.5 0 0 0 5 4 Z "
transform = " matrix(1, 0, 0, 1, 0, 0) " + " ) \" stroke= \" " + color + R " ( " / >
< / svg > ) " ;
}
} // namespace
2020-08-17 13:49:45 +04:00
2017-08-17 11:31:24 +03:00
Panel : : Panel ( not_null < Call * > call )
2020-08-12 17:35:31 +04:00
: _call ( call )
2017-04-19 23:25:48 +03:00
, _user ( call - > user ( ) )
2021-07-27 21:09:13 +03:00
, _layerBg ( std : : make_unique < Ui : : LayerManager > ( widget ( ) ) )
2021-02-03 14:43:31 +04:00
# ifndef Q_OS_MAC
2022-01-18 15:53:04 +03:00
, _controls ( Ui : : Platform : : SetupSeparateTitleControls (
window ( ) ,
2020-08-13 17:58:54 +04:00
st : : callTitle ,
2020-08-13 14:32:11 +04:00
[ = ] ( bool maximized ) { toggleFullScreen ( maximized ) ; } ) )
2021-02-03 14:43:31 +04:00
# endif // !Q_OS_MAC
2020-08-07 17:28:41 +04:00
, _bodySt ( & st : : callBodyLayout )
2020-08-12 17:35:31 +04:00
, _answerHangupRedial ( widget ( ) , st : : callAnswer , & st : : callHangup )
2020-11-28 15:00:06 +03:00
, _decline ( widget ( ) , object_ptr < Ui : : CallButton > ( widget ( ) , st : : callHangup ) )
, _cancel ( widget ( ) , object_ptr < Ui : : CallButton > ( widget ( ) , st : : callCancel ) )
2023-02-27 18:30:50 +03:00
, _screencast (
widget ( ) ,
object_ptr < Ui : : CallButton > (
widget ( ) ,
st : : callScreencastOn ,
& st : : callScreencastOff ) )
2020-08-13 18:45:40 +04:00
, _camera ( widget ( ) , st : : callCameraMute , & st : : callCameraUnmute )
2023-02-27 18:30:50 +03:00
, _mute (
widget ( ) ,
object_ptr < Ui : : CallButton > (
widget ( ) ,
st : : callMicrophoneMute ,
& st : : callMicrophoneUnmute ) )
2020-08-12 17:35:31 +04:00
, _name ( widget ( ) , st : : callName )
, _status ( widget ( ) , st : : callStatus ) {
2021-08-11 11:06:34 +03:00
_layerBg - > setStyleOverrides ( & st : : groupCallBox , & st : : groupCallLayerBox ) ;
_layerBg - > setHideByBackgroundClick ( true ) ;
2017-09-30 21:26:45 +03:00
_decline - > setDuration ( st : : callPanelDuration ) ;
2020-08-14 14:54:15 +04:00
_decline - > entity ( ) - > setText ( tr : : lng_call_decline ( ) ) ;
2017-09-30 21:26:45 +03:00
_cancel - > setDuration ( st : : callPanelDuration ) ;
2020-08-14 14:54:15 +04:00
_cancel - > entity ( ) - > setText ( tr : : lng_call_cancel ( ) ) ;
2023-07-30 13:35:10 +03:00
_screencast - > setDuration ( st : : callPanelDuration ) ;
2017-09-30 21:26:45 +03:00
2020-08-12 17:35:31 +04:00
initWindow ( ) ;
initWidget ( ) ;
2017-04-19 23:25:48 +03:00
initControls ( ) ;
initLayout ( ) ;
2017-04-25 23:36:04 +03:00
showAndActivate ( ) ;
}
2020-05-25 18:16:04 +04:00
Panel : : ~ Panel ( ) = default ;
2023-11-01 22:23:41 +04:00
bool Panel : : isVisible ( ) const {
return window ( ) - > isVisible ( )
2021-06-21 11:29:29 +04:00
& & ! ( window ( ) - > windowState ( ) & Qt : : WindowMinimized ) ;
2020-12-08 21:06:56 +04:00
}
2023-11-01 22:23:41 +04:00
bool Panel : : isActive ( ) const {
return window ( ) - > isActiveWindow ( ) & & isVisible ( ) ;
}
2017-04-25 23:36:04 +03:00
void Panel : : showAndActivate ( ) {
2021-06-21 11:29:29 +04:00
if ( window ( ) - > isHidden ( ) ) {
window ( ) - > show ( ) ;
2020-12-11 11:58:00 +04:00
}
2021-06-21 11:29:29 +04:00
const auto state = window ( ) - > windowState ( ) ;
2020-12-11 11:58:00 +04:00
if ( state & Qt : : WindowMinimized ) {
2021-06-21 11:29:29 +04:00
window ( ) - > setWindowState ( state & ~ Qt : : WindowMinimized ) ;
2020-12-11 11:58:00 +04:00
}
2021-06-21 11:29:29 +04:00
window ( ) - > raise ( ) ;
window ( ) - > activateWindow ( ) ;
window ( ) - > setFocus ( ) ;
2017-04-25 23:36:04 +03:00
}
2020-12-22 20:09:32 +04:00
void Panel : : minimize ( ) {
2021-06-21 11:29:29 +04:00
window ( ) - > setWindowState ( window ( ) - > windowState ( ) | Qt : : WindowMinimized ) ;
2020-12-22 20:09:32 +04:00
}
2023-03-15 16:33:55 +04:00
void Panel : : toggleFullScreen ( ) {
toggleFullScreen ( ! window ( ) - > isFullScreen ( ) ) ;
}
2017-08-17 11:31:24 +03:00
void Panel : : replaceCall ( not_null < Call * > call ) {
2020-08-04 13:06:48 +04:00
reinitWithCall ( call ) ;
2017-05-04 15:29:32 +03:00
updateControlsGeometry ( ) ;
}
2020-08-12 17:35:31 +04:00
void Panel : : initWindow ( ) {
2021-06-21 11:29:29 +04:00
window ( ) - > setAttribute ( Qt : : WA_OpaquePaintEvent ) ;
window ( ) - > setAttribute ( Qt : : WA_NoSystemBackground ) ;
2022-08-09 14:12:19 +03:00
window ( ) - > setTitle ( _user - > name ( ) ) ;
2021-06-21 11:29:29 +04:00
window ( ) - > setTitleStyle ( st : : callTitle ) ;
2020-08-12 17:35:31 +04:00
2024-03-01 07:24:22 +03:00
base : : install_event_filter ( window ( ) . get ( ) , [ = ] ( not_null < QEvent * > e ) {
if ( e - > type ( ) = = QEvent : : Close & & handleClose ( ) ) {
e - > ignore ( ) ;
return base : : EventFilterResult : : Cancel ;
2020-08-12 20:58:24 +04:00
} else if ( e - > type ( ) = = QEvent : : KeyPress ) {
if ( ( static_cast < QKeyEvent * > ( e . get ( ) ) - > key ( ) = = Qt : : Key_Escape )
2021-06-21 11:29:29 +04:00
& & window ( ) - > isFullScreen ( ) ) {
window ( ) - > showNormal ( ) ;
2020-08-12 20:58:24 +04:00
}
2020-08-12 17:35:31 +04:00
}
2024-03-01 07:24:22 +03:00
return base : : EventFilterResult : : Continue ;
} ) ;
2020-08-12 17:35:31 +04:00
2021-06-21 11:29:29 +04:00
window ( ) - > setBodyTitleArea ( [ = ] ( QPoint widgetPoint ) {
2020-08-13 13:02:15 +04:00
using Flag = Ui : : WindowTitleHitTestFlag ;
if ( ! widget ( ) - > rect ( ) . contains ( widgetPoint ) ) {
return Flag : : None | Flag ( 0 ) ;
}
2021-02-03 14:43:31 +04:00
# ifndef Q_OS_MAC
2022-01-18 15:53:04 +03:00
if ( _controls - > controls . geometry ( ) . contains ( widgetPoint ) ) {
2020-08-13 14:32:11 +04:00
return Flag : : None | Flag ( 0 ) ;
}
2021-02-03 14:43:31 +04:00
# endif // !Q_OS_MAC
2020-08-12 17:35:31 +04:00
const auto buttonWidth = st : : callCancel . button . width ;
const auto buttonsWidth = buttonWidth * 4 ;
2020-08-14 16:51:47 +04:00
const auto inControls = ( _fingerprint
& & _fingerprint - > geometry ( ) . contains ( widgetPoint ) )
2020-08-12 20:58:24 +04:00
| | QRect (
2020-08-12 17:35:31 +04:00
( widget ( ) - > width ( ) - buttonsWidth ) / 2 ,
_answerHangupRedial - > y ( ) ,
buttonsWidth ,
_answerHangupRedial - > height ( ) ) . contains ( widgetPoint )
2020-08-12 20:58:24 +04:00
| | ( ! _outgoingPreviewInBody
& & _outgoingVideoBubble - > geometry ( ) . contains ( widgetPoint ) ) ;
2021-08-11 11:06:34 +03:00
if ( inControls ) {
return Flag : : None | Flag ( 0 ) ;
}
const auto shown = _layerBg - > topShownLayer ( ) ;
return ( ! shown | | ! shown - > geometry ( ) . contains ( widgetPoint ) )
2024-07-25 21:25:55 +04:00
? ( Flag : : Move | Flag : : Menu | Flag : : FullScreen )
2021-08-11 11:06:34 +03:00
: Flag : : None ;
2020-08-12 17:35:31 +04:00
} ) ;
2020-08-12 20:58:24 +04:00
2021-06-21 11:29:29 +04:00
// Don't do that, it looks awful :(
//#ifdef Q_OS_WIN
// // On Windows we replace snap-to-top maximizing with fullscreen.
// //
// // We have to switch first to showNormal, so that showFullScreen
// // will remember correct normal window geometry and next showNormal
// // will show it instead of a moving maximized window.
// //
// // We have to do it in InvokeQueued, otherwise it still captures
// // the maximized window geometry and saves it.
// //
// // I couldn't find a less glitchy way to do that *sigh*.
// const auto object = window()->windowHandle();
// const auto signal = &QWindow::windowStateChanged;
// QObject::connect(object, signal, [=](Qt::WindowState state) {
// if (state == Qt::WindowMaximized) {
// InvokeQueued(object, [=] {
// window()->showNormal();
// InvokeQueued(object, [=] {
// window()->showFullScreen();
// });
// });
// }
// });
//#endif // Q_OS_WIN
2017-04-25 23:36:04 +03:00
}
2020-08-12 17:35:31 +04:00
void Panel : : initWidget ( ) {
widget ( ) - > setMouseTracking ( true ) ;
widget ( ) - > paintRequest (
) | rpl : : start_with_next ( [ = ] ( QRect clip ) {
paint ( clip ) ;
} , widget ( ) - > lifetime ( ) ) ;
2020-08-14 16:51:47 +04:00
widget ( ) - > sizeValue (
) | rpl : : skip ( 1 ) | rpl : : start_with_next ( [ = ] {
updateControlsGeometry ( ) ;
2020-08-12 17:35:31 +04:00
} , widget ( ) - > lifetime ( ) ) ;
2017-04-19 23:25:48 +03:00
}
void Panel : : initControls ( ) {
2017-05-05 13:14:40 +03:00
_hangupShown = ( _call - > type ( ) = = Type : : Outgoing ) ;
2023-02-27 18:30:50 +03:00
_mute - > entity ( ) - > setClickedCallback ( [ = ] {
2017-05-04 16:32:56 +03:00
if ( _call ) {
2020-07-16 20:23:55 +04:00
_call - > setMuted ( ! _call - > muted ( ) ) ;
2017-05-04 16:32:56 +03:00
}
2017-04-25 19:45:41 +03:00
} ) ;
2023-02-27 18:30:50 +03:00
_screencast - > entity ( ) - > setClickedCallback ( [ = ] {
2024-01-24 12:42:37 +04:00
const auto env = & Core : : App ( ) . mediaDevices ( ) ;
2021-07-27 21:09:13 +03:00
if ( ! _call ) {
return ;
2024-01-24 12:42:37 +04:00
} else if ( ! env - > desktopCaptureAllowed ( ) ) {
2021-07-27 21:09:13 +03:00
if ( auto box = Group : : ScreenSharingPrivacyRequestBox ( ) ) {
_layerBg - > showBox ( std : : move ( box ) ) ;
}
2024-01-24 12:42:37 +04:00
} else if ( const auto source = env - > uniqueDesktopCaptureSource ( ) ) {
2024-07-25 11:40:22 +02:00
if ( ! chooseSourceActiveDeviceId ( ) . isEmpty ( ) ) {
chooseSourceStop ( ) ;
2021-07-27 21:09:13 +03:00
} else {
chooseSourceAccepted ( * source , false ) ;
}
} else {
Group : : Ui : : DesktopCapture : : ChooseSource ( this ) ;
}
} ) ;
2020-07-16 20:23:55 +04:00
_camera - > setClickedCallback ( [ = ] {
2021-07-27 21:09:13 +03:00
if ( ! _call ) {
return ;
} else {
_call - > toggleCameraSharing ( ! _call - > isSharingCamera ( ) ) ;
2020-07-16 20:23:55 +04:00
}
2017-04-25 19:45:41 +03:00
} ) ;
2020-06-12 16:12:34 +04:00
2017-04-25 19:45:41 +03:00
_updateDurationTimer . setCallback ( [ this ] {
if ( _call ) {
updateStatusText ( _call - > state ( ) ) ;
}
} ) ;
2017-05-07 22:09:20 +03:00
_updateOuterRippleTimer . setCallback ( [ this ] {
if ( _call ) {
2021-07-27 21:09:13 +03:00
_answerHangupRedial - > setOuterValue (
_call - > getWaitingSoundPeakValue ( ) ) ;
2017-05-07 22:09:20 +03:00
} else {
_answerHangupRedial - > setOuterValue ( 0. ) ;
_updateOuterRippleTimer . cancel ( ) ;
}
} ) ;
2017-05-05 13:14:40 +03:00
_answerHangupRedial - > setClickedCallback ( [ this ] {
if ( ! _call | | _hangupShownProgress . animating ( ) ) {
return ;
}
auto state = _call - > state ( ) ;
if ( state = = State : : Busy ) {
_call - > redial ( ) ;
2017-05-08 12:26:43 +03:00
} else if ( _call - > isIncomingWaiting ( ) ) {
2017-05-05 13:14:40 +03:00
_call - > answer ( ) ;
2023-02-27 18:30:50 +03:00
} else if ( state = = State : : WaitingUserConfirmation ) {
2023-03-02 08:52:24 +03:00
_startOutgoingRequests . fire ( false ) ;
2017-05-05 13:14:40 +03:00
} else {
_call - > hangup ( ) ;
}
} ) ;
auto hangupCallback = [ this ] {
if ( _call ) {
_call - > hangup ( ) ;
}
} ;
_decline - > entity ( ) - > setClickedCallback ( hangupCallback ) ;
_cancel - > entity ( ) - > setClickedCallback ( hangupCallback ) ;
2017-05-04 15:29:32 +03:00
2020-08-04 13:06:48 +04:00
reinitWithCall ( _call ) ;
2017-05-05 13:14:40 +03:00
2017-09-30 22:20:40 +03:00
_decline - > finishAnimating ( ) ;
_cancel - > finishAnimating ( ) ;
2023-07-30 13:35:10 +03:00
_screencast - > finishAnimating ( ) ;
2020-08-04 13:06:48 +04:00
}
2020-05-19 10:33:57 +04:00
2020-08-13 15:32:25 +04:00
void Panel : : setIncomingSize ( QSize size ) {
if ( _incomingFrameSize = = size ) {
2020-08-04 13:06:48 +04:00
return ;
}
2020-08-13 15:32:25 +04:00
_incomingFrameSize = size ;
2020-08-17 13:49:45 +04:00
refreshIncomingGeometry ( ) ;
2020-08-12 17:35:31 +04:00
showControls ( ) ;
2017-05-04 15:29:32 +03:00
}
2021-07-27 21:09:13 +03:00
QWidget * Panel : : chooseSourceParent ( ) {
return window ( ) . get ( ) ;
}
QString Panel : : chooseSourceActiveDeviceId ( ) {
return _call - > screenSharingDeviceId ( ) ;
}
bool Panel : : chooseSourceActiveWithAudio ( ) {
return false ; // _call->screenSharingWithAudio();
}
bool Panel : : chooseSourceWithAudioSupported ( ) {
//#ifdef Q_OS_WIN
// return true;
//#else // Q_OS_WIN
return false ;
//#endif // Q_OS_WIN
}
rpl : : lifetime & Panel : : chooseSourceInstanceLifetime ( ) {
return lifetime ( ) ;
}
2023-02-27 18:30:50 +03:00
rpl : : producer < bool > Panel : : startOutgoingRequests ( ) const {
2023-03-02 08:52:24 +03:00
return _startOutgoingRequests . events (
) | rpl : : filter ( [ = ] {
2023-02-27 18:30:50 +03:00
return _call & & ( _call - > state ( ) = = State : : WaitingUserConfirmation ) ;
2023-03-02 08:52:24 +03:00
} ) ;
2023-02-27 18:30:50 +03:00
}
2021-07-27 21:09:13 +03:00
void Panel : : chooseSourceAccepted (
const QString & deviceId ,
bool withAudio ) {
_call - > toggleScreenSharing ( deviceId /*, withAudio*/ ) ;
}
void Panel : : chooseSourceStop ( ) {
_call - > toggleScreenSharing ( std : : nullopt ) ;
}
2020-08-17 13:49:45 +04:00
void Panel : : refreshIncomingGeometry ( ) {
Expects ( _call ! = nullptr ) ;
Expects ( _incoming ! = nullptr ) ;
if ( _incomingFrameSize . isEmpty ( ) ) {
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > hide ( ) ;
2020-08-17 13:49:45 +04:00
return ;
}
const auto to = widget ( ) - > size ( ) ;
2022-09-05 19:03:52 +04:00
const auto use = : : Media : : Streaming : : DecideFrameResize (
2020-08-17 13:49:45 +04:00
to ,
2022-09-05 19:03:52 +04:00
_incomingFrameSize
) . result ;
2020-08-17 13:49:45 +04:00
const auto pos = QPoint (
( to . width ( ) - use . width ( ) ) / 2 ,
( to . height ( ) - use . height ( ) ) / 2 ) ;
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > setGeometry ( QRect ( pos , use ) ) ;
_incoming - > widget ( ) - > show ( ) ;
2020-08-17 13:49:45 +04:00
}
2020-08-04 13:06:48 +04:00
void Panel : : reinitWithCall ( Call * call ) {
_callLifetime . destroy ( ) ;
_call = call ;
if ( ! _call ) {
2020-08-17 13:49:45 +04:00
_incoming = nullptr ;
2020-08-06 20:55:58 +04:00
_outgoingVideoBubble = nullptr ;
2022-02-04 09:43:56 +03:00
_powerSaveBlocker = nullptr ;
2020-08-04 13:06:48 +04:00
return ;
}
_user = _call - > user ( ) ;
2020-08-06 20:55:58 +04:00
auto remoteMuted = _call - > remoteAudioStateValue (
2024-03-21 21:03:21 +03:00
) | rpl : : map ( rpl : : mappers : : _1 = = Call : : RemoteAudioState : : Muted ) ;
2020-08-14 12:32:05 +04:00
rpl : : duplicate (
remoteMuted
) | rpl : : start_with_next ( [ = ] ( bool muted ) {
if ( muted ) {
createRemoteAudioMute ( ) ;
} else {
_remoteAudioMute . destroy ( ) ;
2024-03-21 21:03:21 +03:00
showRemoteLowBattery ( ) ;
}
} , _callLifetime ) ;
_call - > remoteBatteryStateValue (
) | rpl : : start_with_next ( [ = ] ( Call : : RemoteBatteryState state ) {
if ( state = = Call : : RemoteBatteryState : : Low ) {
createRemoteLowBattery ( ) ;
} else {
_remoteLowBattery . destroy ( ) ;
2020-08-14 12:32:05 +04:00
}
} , _callLifetime ) ;
2020-08-12 17:35:31 +04:00
_userpic = std : : make_unique < Userpic > (
widget ( ) ,
_user ,
std : : move ( remoteMuted ) ) ;
2020-08-06 20:55:58 +04:00
_outgoingVideoBubble = std : : make_unique < VideoBubble > (
2020-08-12 17:35:31 +04:00
widget ( ) ,
2020-08-06 20:55:58 +04:00
_call - > videoOutgoing ( ) ) ;
2020-08-17 13:49:45 +04:00
_incoming = std : : make_unique < Incoming > (
widget ( ) ,
2021-06-08 15:51:52 +04:00
_call - > videoIncoming ( ) ,
2021-06-21 11:29:29 +04:00
_window . backend ( ) ) ;
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > hide ( ) ;
2020-08-06 20:55:58 +04:00
2020-08-04 13:06:48 +04:00
_call - > mutedValue (
) | rpl : : start_with_next ( [ = ] ( bool mute ) {
2023-02-27 18:30:50 +03:00
_mute - > entity ( ) - > setProgress ( mute ? 1. : 0. ) ;
_mute - > entity ( ) - > setText ( mute
2020-08-14 14:54:15 +04:00
? tr : : lng_call_unmute_audio ( )
: tr : : lng_call_mute_audio ( ) ) ;
2020-08-04 13:06:48 +04:00
} , _callLifetime ) ;
2020-08-05 16:11:18 +04:00
_call - > videoOutgoing ( ) - > stateValue (
2021-07-27 21:09:13 +03:00
) | rpl : : start_with_next ( [ = ] {
{
const auto active = _call - > isSharingCamera ( ) ;
_camera - > setProgress ( active ? 0. : 1. ) ;
_camera - > setText ( active
? tr : : lng_call_stop_video ( )
: tr : : lng_call_start_video ( ) ) ;
}
{
const auto active = _call - > isSharingScreen ( ) ;
2023-02-27 18:30:50 +03:00
_screencast - > entity ( ) - > setProgress ( active ? 0. : 1. ) ;
_screencast - > entity ( ) - > setText ( tr : : lng_call_screencast ( ) ) ;
2022-12-12 23:32:51 +03:00
_outgoingVideoBubble - > setMirrored ( ! active ) ;
2021-07-27 21:09:13 +03:00
}
2020-08-04 13:06:48 +04:00
} , _callLifetime ) ;
2017-05-04 16:32:56 +03:00
2020-06-25 21:57:36 +04:00
_call - > stateValue (
) | rpl : : start_with_next ( [ = ] ( State state ) {
stateChanged ( state ) ;
2020-08-04 13:06:48 +04:00
} , _callLifetime ) ;
2020-08-13 15:32:25 +04:00
_call - > videoIncoming ( ) - > renderNextFrame (
2020-08-04 13:06:48 +04:00
) | rpl : : start_with_next ( [ = ] {
2021-01-27 21:19:04 +04:00
const auto track = _call - > videoIncoming ( ) ;
2021-06-10 17:16:17 +04:00
setIncomingSize ( track - > state ( ) = = Webrtc : : VideoState : : Active
? track - > frameSize ( )
: QSize ( ) ) ;
2021-05-19 16:59:31 +04:00
if ( _incoming - > widget ( ) - > isHidden ( ) ) {
2020-08-13 15:32:25 +04:00
return ;
}
const auto incoming = incomingFrameGeometry ( ) ;
const auto outgoing = outgoingFrameGeometry ( ) ;
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > update ( ) ;
2020-08-13 15:32:25 +04:00
if ( incoming . intersects ( outgoing ) ) {
2020-08-17 13:49:45 +04:00
widget ( ) - > update ( outgoing ) ;
2020-08-13 15:32:25 +04:00
}
} , _callLifetime ) ;
2021-06-10 17:16:17 +04:00
_call - > videoIncoming ( ) - > stateValue (
) | rpl : : start_with_next ( [ = ] ( Webrtc : : VideoState state ) {
setIncomingSize ( ( state = = Webrtc : : VideoState : : Active )
? _call - > videoIncoming ( ) - > frameSize ( )
: QSize ( ) ) ;
} , _callLifetime ) ;
2020-08-13 15:32:25 +04:00
_call - > videoOutgoing ( ) - > renderNextFrame (
) | rpl : : start_with_next ( [ = ] {
const auto incoming = incomingFrameGeometry ( ) ;
const auto outgoing = outgoingFrameGeometry ( ) ;
2020-08-17 13:49:45 +04:00
widget ( ) - > update ( outgoing ) ;
2020-08-13 15:32:25 +04:00
if ( incoming . intersects ( outgoing ) ) {
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > update ( ) ;
2020-08-13 15:32:25 +04:00
}
2020-08-06 20:55:58 +04:00
} , _callLifetime ) ;
2018-05-27 11:24:47 +03:00
2020-08-07 17:28:41 +04:00
rpl : : combine (
_call - > stateValue ( ) ,
2023-03-02 08:52:24 +03:00
rpl : : single (
rpl : : empty_value ( )
) | rpl : : then ( _call - > videoOutgoing ( ) - > renderNextFrame ( ) )
2020-08-07 17:28:41 +04:00
) | rpl : : start_with_next ( [ = ] ( State state , auto ) {
if ( state ! = State : : Ended
& & state ! = State : : EndedByOtherDevice
& & state ! = State : : Failed
& & state ! = State : : FailedHangingUp
& & state ! = State : : HangingUp ) {
refreshOutgoingPreviewInBody ( state ) ;
}
} , _callLifetime ) ;
2020-08-21 16:34:37 +04:00
_call - > errors (
) | rpl : : start_with_next ( [ = ] ( Error error ) {
const auto text = [ = ] {
switch ( error . type ) {
case ErrorType : : NoCamera :
return tr : : lng_call_error_no_camera ( tr : : now ) ;
case ErrorType : : NotVideoCall :
2022-08-09 14:12:19 +03:00
return tr : : lng_call_error_camera_outdated (
tr : : now ,
lt_user ,
_user - > name ( ) ) ;
2020-08-21 16:34:37 +04:00
case ErrorType : : NotStartedCall :
return tr : : lng_call_error_camera_not_started ( tr : : now ) ;
//case ErrorType::NoMicrophone:
// return tr::lng_call_error_no_camera(tr::now);
case ErrorType : : Unknown :
return Lang : : Hard : : CallErrorIncompatible ( ) ;
}
Unexpected ( " Error type in _call->errors(). " ) ;
} ( ) ;
Ui : : Toast : : Show ( widget ( ) , Ui : : Toast : : Config {
. text = { text } ,
. st = & st : : callErrorToast ,
} ) ;
} , _callLifetime ) ;
2022-08-09 14:12:19 +03:00
_name - > setText ( _user - > name ( ) ) ;
2017-05-04 15:29:32 +03:00
updateStatusText ( _call - > state ( ) ) ;
2020-08-17 13:49:45 +04:00
2021-06-10 17:16:17 +04:00
_answerHangupRedial - > raise ( ) ;
_decline - > raise ( ) ;
_cancel - > raise ( ) ;
_camera - > raise ( ) ;
2023-03-02 08:52:24 +03:00
if ( _startVideo ) {
_startVideo - > raise ( ) ;
}
2021-06-10 17:16:17 +04:00
_mute - > raise ( ) ;
2022-02-04 09:43:56 +03:00
_powerSaveBlocker = std : : make_unique < base : : PowerSaveBlocker > (
base : : PowerSaveBlockType : : PreventDisplaySleep ,
u " Video call is active " _q ,
window ( ) - > windowHandle ( ) ) ;
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > lower ( ) ;
2017-04-19 23:25:48 +03:00
}
2020-08-14 12:32:05 +04:00
void Panel : : createRemoteAudioMute ( ) {
_remoteAudioMute . create (
widget ( ) ,
object_ptr < Ui : : FlatLabel > (
widget ( ) ,
tr : : lng_call_microphone_off (
lt_user ,
2024-03-21 21:03:21 +03:00
_user - > session ( ) . changes ( ) . peerFlagsValue (
_user ,
Data : : PeerUpdate : : Flag : : Name
) | rpl : : map ( [ = ] { return _user - > shortName ( ) ; } ) ) ,
2020-08-14 12:32:05 +04:00
st : : callRemoteAudioMute ) ,
st : : callTooltipPadding ) ;
2020-08-14 16:51:47 +04:00
_remoteAudioMute - > setAttribute ( Qt : : WA_TransparentForMouseEvents ) ;
2020-08-14 12:32:05 +04:00
_remoteAudioMute - > paintRequest (
) | rpl : : start_with_next ( [ = ] {
auto p = QPainter ( _remoteAudioMute ) ;
2024-03-21 21:03:21 +03:00
const auto r = _remoteAudioMute - > rect ( ) ;
2020-08-14 12:32:05 +04:00
auto hq = PainterHighQualityEnabler ( p ) ;
2020-08-14 14:54:15 +04:00
p . setBrush ( st : : videoPlayIconBg ) ;
2020-08-14 12:32:05 +04:00
p . setPen ( Qt : : NoPen ) ;
2024-03-21 21:03:21 +03:00
p . drawRoundedRect ( r , r . height ( ) / 2 , r . height ( ) / 2 ) ;
2020-08-14 12:32:05 +04:00
st : : callTooltipMutedIcon . paint (
p ,
st : : callTooltipMutedIconPosition ,
_remoteAudioMute - > width ( ) ) ;
} , _remoteAudioMute - > lifetime ( ) ) ;
showControls ( ) ;
updateControlsGeometry ( ) ;
}
2024-03-21 21:03:21 +03:00
void Panel : : createRemoteLowBattery ( ) {
_remoteLowBattery . create (
widget ( ) ,
object_ptr < Ui : : FlatLabel > (
widget ( ) ,
tr : : lng_call_battery_level_low (
lt_user ,
_user - > session ( ) . changes ( ) . peerFlagsValue (
_user ,
Data : : PeerUpdate : : Flag : : Name
) | rpl : : map ( [ = ] { return _user - > shortName ( ) ; } ) ) ,
st : : callRemoteAudioMute ) ,
st : : callTooltipPadding ) ;
_remoteLowBattery - > setAttribute ( Qt : : WA_TransparentForMouseEvents ) ;
style : : PaletteChanged (
) | rpl : : start_with_next ( [ = ] {
_remoteLowBattery . destroy ( ) ;
createRemoteLowBattery ( ) ;
} , _remoteLowBattery - > lifetime ( ) ) ;
constexpr auto kBatterySize = QSize ( 29 , 13 ) ;
const auto icon = [ & ] {
auto svg = QSvgRenderer (
BatterySvg ( kBatterySize , st : : videoPlayIconFg - > c ) ) ;
auto image = QImage (
kBatterySize * style : : DevicePixelRatio ( ) ,
QImage : : Format_ARGB32_Premultiplied ) ;
image . setDevicePixelRatio ( style : : DevicePixelRatio ( ) ) ;
image . fill ( Qt : : transparent ) ;
{
auto p = QPainter ( & image ) ;
svg . render ( & p , Rect ( kBatterySize ) ) ;
}
return image ;
} ( ) ;
_remoteLowBattery - > paintRequest (
) | rpl : : start_with_next ( [ = ] {
auto p = QPainter ( _remoteLowBattery ) ;
const auto r = _remoteLowBattery - > rect ( ) ;
auto hq = PainterHighQualityEnabler ( p ) ;
p . setBrush ( st : : videoPlayIconBg ) ;
p . setPen ( Qt : : NoPen ) ;
p . drawRoundedRect ( r , r . height ( ) / 2 , r . height ( ) / 2 ) ;
p . drawImage (
st : : callTooltipMutedIconPosition . x ( ) ,
( r . height ( ) - kBatterySize . height ( ) ) / 2 ,
icon ) ;
} , _remoteLowBattery - > lifetime ( ) ) ;
showControls ( ) ;
updateControlsGeometry ( ) ;
}
void Panel : : showRemoteLowBattery ( ) {
if ( _remoteLowBattery ) {
_remoteLowBattery - > setVisible ( ! _remoteAudioMute
| | _remoteAudioMute - > isHidden ( ) ) ;
}
}
2017-04-19 23:25:48 +03:00
void Panel : : initLayout ( ) {
initGeometry ( ) ;
2020-08-13 17:58:54 +04:00
_name - > setAttribute ( Qt : : WA_TransparentForMouseEvents ) ;
_status - > setAttribute ( Qt : : WA_TransparentForMouseEvents ) ;
2020-06-12 16:12:34 +04:00
using UpdateFlag = Data : : PeerUpdate : : Flag ;
_user - > session ( ) . changes ( ) . peerUpdates (
2020-08-06 20:55:58 +04:00
UpdateFlag : : Name
2020-06-12 16:12:34 +04:00
) | rpl : : filter ( [ = ] ( const Data : : PeerUpdate & update ) {
// _user may change for the same Panel.
return ( _call ! = nullptr ) & & ( update . peer = = _user ) ;
} ) | rpl : : start_with_next ( [ = ] ( const Data : : PeerUpdate & update ) {
2022-08-09 14:12:19 +03:00
_name - > setText ( _call - > user ( ) - > name ( ) ) ;
2020-08-06 20:55:58 +04:00
updateControlsGeometry ( ) ;
2020-08-12 17:35:31 +04:00
} , widget ( ) - > lifetime ( ) ) ;
2020-08-13 14:32:11 +04:00
2021-02-03 14:43:31 +04:00
# ifndef Q_OS_MAC
2022-01-18 15:53:04 +03:00
_controls - > wrap . raise ( ) ;
2021-02-03 14:43:31 +04:00
# endif // !Q_OS_MAC
2017-05-04 16:32:56 +03:00
}
2017-05-05 13:14:40 +03:00
void Panel : : showControls ( ) {
Expects ( _call ! = nullptr ) ;
2020-05-28 18:32:10 +04:00
2020-08-12 17:35:31 +04:00
widget ( ) - > showChildren ( ) ;
2017-09-30 22:20:40 +03:00
_decline - > setVisible ( _decline - > toggled ( ) ) ;
_cancel - > setVisible ( _cancel - > toggled ( ) ) ;
2023-07-30 13:35:10 +03:00
_screencast - > setVisible ( _screencast - > toggled ( ) ) ;
2020-08-13 15:32:25 +04:00
const auto shown = ! _incomingFrameSize . isEmpty ( ) ;
2021-05-19 16:59:31 +04:00
_incoming - > widget ( ) - > setVisible ( shown ) ;
2020-08-13 15:32:25 +04:00
_name - > setVisible ( ! shown ) ;
_status - > setVisible ( ! shown ) ;
_userpic - > setVisible ( ! shown ) ;
2020-08-14 12:32:05 +04:00
if ( _remoteAudioMute ) {
_remoteAudioMute - > setVisible ( shown ) ;
}
2024-03-21 21:03:21 +03:00
showRemoteLowBattery ( ) ;
2017-05-05 13:14:40 +03:00
}
2020-08-14 19:59:24 +04:00
void Panel : : closeBeforeDestroy ( ) {
2021-06-21 11:29:29 +04:00
window ( ) - > close ( ) ;
2020-08-04 13:06:48 +04:00
reinitWithCall ( nullptr ) ;
2017-05-04 16:32:56 +03:00
}
2021-06-21 11:29:29 +04:00
rpl : : lifetime & Panel : : lifetime ( ) {
return window ( ) - > lifetime ( ) ;
}
2017-04-19 23:25:48 +03:00
void Panel : : initGeometry ( ) {
2020-05-14 20:27:51 +04:00
const auto center = Core : : App ( ) . getPointForCallPanelCenter ( ) ;
2020-08-12 17:35:31 +04:00
const auto initRect = QRect ( 0 , 0 , st : : callWidth , st : : callHeight ) ;
2021-06-21 11:29:29 +04:00
window ( ) - > setGeometry ( initRect . translated ( center - initRect . center ( ) ) ) ;
window ( ) - > setMinimumSize ( { st : : callWidthMin , st : : callHeightMin } ) ;
window ( ) - > show ( ) ;
2017-04-25 19:45:41 +03:00
updateControlsGeometry ( ) ;
2017-04-19 23:25:48 +03:00
}
2020-08-07 17:28:41 +04:00
void Panel : : refreshOutgoingPreviewInBody ( State state ) {
const auto inBody = ( state ! = State : : Established )
2020-08-11 13:59:48 +04:00
& & ( _call - > videoOutgoing ( ) - > state ( ) ! = Webrtc : : VideoState : : Inactive )
2020-08-07 17:28:41 +04:00
& & ! _call - > videoOutgoing ( ) - > frameSize ( ) . isEmpty ( ) ;
if ( _outgoingPreviewInBody = = inBody ) {
return ;
}
_outgoingPreviewInBody = inBody ;
_bodySt = inBody ? & st : : callBodyWithPreview : & st : : callBodyLayout ;
updateControlsGeometry ( ) ;
}
2020-08-13 14:32:11 +04:00
void Panel : : toggleFullScreen ( bool fullscreen ) {
if ( fullscreen ) {
2021-06-21 11:29:29 +04:00
window ( ) - > showFullScreen ( ) ;
2020-08-13 14:32:11 +04:00
} else {
2021-06-21 11:29:29 +04:00
window ( ) - > showNormal ( ) ;
2020-08-13 14:32:11 +04:00
}
}
2020-08-13 15:32:25 +04:00
QRect Panel : : incomingFrameGeometry ( ) const {
2021-05-19 16:59:31 +04:00
return ( ! _incoming | | _incoming - > widget ( ) - > isHidden ( ) )
2020-08-17 13:49:45 +04:00
? QRect ( )
2021-05-19 16:59:31 +04:00
: _incoming - > widget ( ) - > geometry ( ) ;
2020-08-13 15:32:25 +04:00
}
QRect Panel : : outgoingFrameGeometry ( ) const {
return _outgoingVideoBubble - > geometry ( ) ;
}
2020-08-12 17:35:31 +04:00
void Panel : : updateControlsGeometry ( ) {
2020-08-13 15:16:39 +04:00
if ( widget ( ) - > size ( ) . isEmpty ( ) ) {
2017-04-19 23:25:48 +03:00
return ;
}
2020-08-17 13:49:45 +04:00
if ( _incoming ) {
refreshIncomingGeometry ( ) ;
}
2020-08-14 16:51:47 +04:00
if ( _fingerprint ) {
2021-02-03 14:43:31 +04:00
# ifndef Q_OS_MAC
2022-01-18 15:53:04 +03:00
const auto controlsGeometry = _controls - > controls . geometry ( ) ;
2021-06-10 17:16:17 +04:00
const auto halfWidth = widget ( ) - > width ( ) / 2 ;
const auto minLeft = ( controlsGeometry . center ( ) . x ( ) < halfWidth )
? ( controlsGeometry . width ( ) + st : : callFingerprintTop )
: 0 ;
const auto minRight = ( controlsGeometry . center ( ) . x ( ) > = halfWidth )
? ( controlsGeometry . width ( ) + st : : callFingerprintTop )
: 0 ;
_incoming - > setControlsAlignment ( minLeft
? style : : al_left
: style : : al_right ) ;
2021-02-03 14:43:31 +04:00
# else // !Q_OS_MAC
2021-06-10 17:16:17 +04:00
const auto minLeft = 0 ;
2020-08-14 16:51:47 +04:00
const auto minRight = 0 ;
# endif // _controls
const auto desired = ( widget ( ) - > width ( ) - _fingerprint - > width ( ) ) / 2 ;
2021-06-10 17:16:17 +04:00
if ( minLeft ) {
_fingerprint - > moveToLeft (
std : : max ( desired , minLeft ) ,
st : : callFingerprintTop ) ;
} else {
_fingerprint - > moveToRight (
std : : max ( desired , minRight ) ,
st : : callFingerprintTop ) ;
}
2020-08-14 16:51:47 +04:00
}
2020-08-13 15:16:39 +04:00
const auto innerHeight = std : : max ( widget ( ) - > height ( ) , st : : callHeightMin ) ;
const auto innerWidth = widget ( ) - > width ( ) - 2 * st : : callInnerPadding ;
2020-08-14 16:51:47 +04:00
const auto availableTop = st : : callFingerprintTop
+ ( _fingerprint ? _fingerprint - > height ( ) : 0 )
+ st : : callFingerprintBottom ;
2020-08-12 17:35:31 +04:00
const auto available = widget ( ) - > height ( )
- st : : callBottomControlsHeight
2020-08-07 17:28:41 +04:00
- availableTop ;
const auto bodyPreviewSizeMax = st : : callOutgoingPreviewMin
+ ( ( st : : callOutgoingPreview
- st : : callOutgoingPreviewMin )
* ( innerHeight - st : : callHeightMin )
/ ( st : : callHeight - st : : callHeightMin ) ) ;
const auto bodyPreviewSize = QSize (
2020-08-13 15:16:39 +04:00
std : : min (
bodyPreviewSizeMax . width ( ) ,
std : : min ( innerWidth , st : : callOutgoingPreviewMax . width ( ) ) ) ,
std : : min (
bodyPreviewSizeMax . height ( ) ,
st : : callOutgoingPreviewMax . height ( ) ) ) ;
2020-08-07 17:28:41 +04:00
const auto contentHeight = _bodySt - > height
+ ( _outgoingPreviewInBody ? bodyPreviewSize . height ( ) : 0 ) ;
const auto remainingHeight = available - contentHeight ;
const auto skipHeight = remainingHeight
/ ( _outgoingPreviewInBody ? 3 : 2 ) ;
_bodyTop = availableTop + skipHeight ;
_buttonsTop = availableTop + available ;
const auto previewTop = _bodyTop + _bodySt - > height + skipHeight ;
_userpic - > setGeometry (
2020-08-12 17:35:31 +04:00
( widget ( ) - > width ( ) - _bodySt - > photoSize ) / 2 ,
2020-08-07 17:28:41 +04:00
_bodyTop + _bodySt - > photoTop ,
_bodySt - > photoSize ) ;
2020-08-13 19:48:20 +04:00
_userpic - > setMuteLayout (
_bodySt - > mutePosition ,
_bodySt - > muteSize ,
_bodySt - > muteStroke ) ;
2020-08-07 17:28:41 +04:00
_name - > moveToLeft (
2020-08-12 17:35:31 +04:00
( widget ( ) - > width ( ) - _name - > width ( ) ) / 2 ,
2020-08-07 17:28:41 +04:00
_bodyTop + _bodySt - > nameTop ) ;
2017-04-25 19:45:41 +03:00
updateStatusGeometry ( ) ;
2020-08-14 12:32:05 +04:00
if ( _remoteAudioMute ) {
_remoteAudioMute - > moveToLeft (
( widget ( ) - > width ( ) - _remoteAudioMute - > width ( ) ) / 2 ,
( _buttonsTop
- st : : callRemoteAudioMuteSkip
- _remoteAudioMute - > height ( ) ) ) ;
}
2024-03-21 21:03:21 +03:00
if ( _remoteLowBattery ) {
_remoteLowBattery - > moveToLeft (
( widget ( ) - > width ( ) - _remoteLowBattery - > width ( ) ) / 2 ,
( _buttonsTop
- st : : callRemoteAudioMuteSkip
- _remoteLowBattery - > height ( ) ) ) ;
}
2020-08-14 12:32:05 +04:00
2020-08-07 17:28:41 +04:00
if ( _outgoingPreviewInBody ) {
_outgoingVideoBubble - > updateGeometry (
VideoBubble : : DragMode : : None ,
QRect (
2020-08-12 17:35:31 +04:00
( widget ( ) - > width ( ) - bodyPreviewSize . width ( ) ) / 2 ,
2020-08-07 17:28:41 +04:00
previewTop ,
bodyPreviewSize . width ( ) ,
bodyPreviewSize . height ( ) ) ) ;
} else {
updateOutgoingVideoBubbleGeometry ( ) ;
}
2020-08-06 20:55:58 +04:00
2017-05-05 13:14:40 +03:00
updateHangupGeometry ( ) ;
2017-04-19 23:25:48 +03:00
}
2020-08-07 17:28:41 +04:00
void Panel : : updateOutgoingVideoBubbleGeometry ( ) {
Expects ( ! _outgoingPreviewInBody ) ;
const auto size = st : : callOutgoingDefaultSize ;
_outgoingVideoBubble - > updateGeometry (
VideoBubble : : DragMode : : SnapToCorners ,
2024-03-21 21:03:21 +03:00
widget ( ) - > rect ( ) - Margins ( st : : callInnerPadding ) ,
2020-08-07 17:28:41 +04:00
size ) ;
}
2017-05-05 13:14:40 +03:00
void Panel : : updateHangupGeometry ( ) {
2023-07-30 13:35:10 +03:00
const auto isWaitingUser = ( _call
& & _call - > state ( ) = = State : : WaitingUserConfirmation ) ;
const auto hangupProgress = isWaitingUser
2023-02-27 18:30:50 +03:00
? 0.
: _hangupShownProgress . value ( _hangupShown ? 1. : 0. ) ;
2017-05-05 13:14:40 +03:00
_answerHangupRedial - > setProgress ( hangupProgress ) ;
2023-07-30 13:35:10 +03:00
// Screencast - Camera - Cancel/Decline - Answer/Hangup/Redial - Mute.
const auto buttonWidth = st : : callCancel . button . width ;
const auto cancelWidth = buttonWidth * ( 1. - hangupProgress ) ;
const auto cancelLeft = ( isWaitingUser )
? ( ( widget ( ) - > width ( ) - buttonWidth ) / 2 )
: ( _mute - > animating ( ) )
? ( ( widget ( ) - > width ( ) - cancelWidth ) / 2 )
: ( ( widget ( ) - > width ( ) / 2 ) - cancelWidth ) ;
_cancel - > moveToLeft ( cancelLeft , _buttonsTop ) ;
_decline - > moveToLeft ( cancelLeft , _buttonsTop ) ;
_camera - > moveToLeft ( cancelLeft - buttonWidth , _buttonsTop ) ;
_screencast - > moveToLeft ( _camera - > x ( ) - buttonWidth , _buttonsTop ) ;
_answerHangupRedial - > moveToLeft ( cancelLeft + cancelWidth , _buttonsTop ) ;
_mute - > moveToLeft ( _answerHangupRedial - > x ( ) + buttonWidth , _buttonsTop ) ;
2023-03-02 08:52:24 +03:00
if ( _startVideo ) {
2023-02-27 18:30:50 +03:00
_startVideo - > moveToLeft ( _camera - > x ( ) , _camera - > y ( ) ) ;
}
2017-05-05 13:14:40 +03:00
}
2017-04-25 19:45:41 +03:00
void Panel : : updateStatusGeometry ( ) {
2020-08-12 17:35:31 +04:00
_status - > moveToLeft (
( widget ( ) - > width ( ) - _status - > width ( ) ) / 2 ,
_bodyTop + _bodySt - > statusTop ) ;
2017-04-25 19:45:41 +03:00
}
2020-08-12 17:35:31 +04:00
void Panel : : paint ( QRect clip ) {
2022-09-17 00:23:27 +04:00
auto p = QPainter ( widget ( ) ) ;
2017-05-04 16:32:56 +03:00
2020-08-17 13:49:45 +04:00
auto region = QRegion ( clip ) ;
2021-05-19 16:59:31 +04:00
if ( ! _incoming - > widget ( ) - > isHidden ( ) ) {
region = region . subtracted ( QRegion ( _incoming - > widget ( ) - > geometry ( ) ) ) ;
2020-08-17 13:49:45 +04:00
}
2021-09-08 13:53:54 +03:00
for ( const auto & rect : region ) {
2020-08-17 13:49:45 +04:00
p . fillRect ( rect , st : : callBgOpaque ) ;
}
2021-05-19 16:59:31 +04:00
if ( _incoming & & _incoming - > widget ( ) - > isHidden ( ) ) {
2020-08-17 13:49:45 +04:00
_call - > videoIncoming ( ) - > markFrameShown ( ) ;
2020-07-31 18:36:35 +04:00
}
2018-05-27 11:24:47 +03:00
}
2024-03-01 07:24:22 +03:00
bool Panel : : handleClose ( ) const {
2017-05-15 11:17:59 +03:00
if ( _call ) {
2024-03-01 07:24:22 +03:00
window ( ) - > hide ( ) ;
return true ;
2017-05-15 11:17:59 +03:00
}
2024-03-01 07:24:22 +03:00
return false ;
2017-05-12 18:27:19 +03:00
}
2021-07-28 16:24:11 +03:00
not_null < Ui : : RpWindow * > Panel : : window ( ) const {
2021-06-21 11:29:29 +04:00
return _window . window ( ) ;
}
2020-08-12 17:35:31 +04:00
not_null < Ui : : RpWidget * > Panel : : widget ( ) const {
2021-06-21 11:29:29 +04:00
return _window . widget ( ) ;
2017-04-29 21:41:41 +03:00
}
2017-04-25 19:45:41 +03:00
void Panel : : stateChanged ( State state ) {
2020-08-06 20:55:58 +04:00
Expects ( _call ! = nullptr ) ;
2017-04-25 19:45:41 +03:00
updateStatusText ( state ) ;
2017-04-29 21:00:27 +03:00
2020-08-06 20:55:58 +04:00
if ( ( state ! = State : : HangingUp )
& & ( state ! = State : : Ended )
& & ( state ! = State : : EndedByOtherDevice )
& & ( state ! = State : : FailedHangingUp )
& & ( state ! = State : : Failed ) ) {
2023-02-27 18:30:50 +03:00
const auto isBusy = ( state = = State : : Busy ) ;
const auto isWaitingUser = ( state = = State : : WaitingUserConfirmation ) ;
if ( isBusy ) {
2022-02-04 09:43:56 +03:00
_powerSaveBlocker = nullptr ;
}
2023-03-02 08:52:24 +03:00
if ( _startVideo & & ! isWaitingUser ) {
_startVideo = nullptr ;
} else if ( ! _startVideo & & isWaitingUser ) {
_startVideo = base : : make_unique_q < Ui : : CallButton > (
widget ( ) ,
st : : callStartVideo ) ;
_startVideo - > setText ( tr : : lng_call_start_video ( ) ) ;
_startVideo - > clicks ( ) | rpl : : map_to ( true ) | rpl : : start_to_stream (
_startOutgoingRequests ,
_startVideo - > lifetime ( ) ) ;
2023-02-27 18:30:50 +03:00
}
2023-03-02 08:52:24 +03:00
_camera - > setVisible ( ! _startVideo ) ;
2022-02-04 09:43:56 +03:00
2023-02-27 18:30:50 +03:00
const auto toggleButton = [ & ] ( auto & & button , bool visible ) {
2020-08-06 20:55:58 +04:00
button - > toggle (
visible ,
2021-06-21 11:29:29 +04:00
window ( ) - > isHidden ( )
2020-08-06 20:55:58 +04:00
? anim : : type : : instant
: anim : : type : : normal ) ;
} ;
2023-02-27 18:30:50 +03:00
const auto incomingWaiting = _call - > isIncomingWaiting ( ) ;
2020-08-06 20:55:58 +04:00
if ( incomingWaiting ) {
_updateOuterRippleTimer . callEach ( Call : : kSoundSampleMs ) ;
}
toggleButton ( _decline , incomingWaiting ) ;
2023-02-27 18:30:50 +03:00
toggleButton ( _cancel , ( isBusy | | isWaitingUser ) ) ;
toggleButton ( _mute , ! isWaitingUser ) ;
2023-07-30 13:35:10 +03:00
toggleButton (
_screencast ,
! ( isBusy | | isWaitingUser | | incomingWaiting ) ) ;
2023-02-27 18:30:50 +03:00
const auto hangupShown = ! _decline - > toggled ( )
2020-08-06 20:55:58 +04:00
& & ! _cancel - > toggled ( ) ;
if ( _hangupShown ! = hangupShown ) {
_hangupShown = hangupShown ;
2023-02-27 18:30:50 +03:00
_hangupShownProgress . start (
[ this ] { updateHangupGeometry ( ) ; } ,
_hangupShown ? 0. : 1. ,
_hangupShown ? 1. : 0. ,
st : : callPanelDuration ,
anim : : sineInOut ) ;
2020-08-06 20:55:58 +04:00
}
2020-08-14 14:54:15 +04:00
const auto answerHangupRedialState = incomingWaiting
? AnswerHangupRedialState : : Answer
2023-02-27 18:30:50 +03:00
: isBusy
2020-08-14 14:54:15 +04:00
? AnswerHangupRedialState : : Redial
2023-02-27 18:30:50 +03:00
: isWaitingUser
? AnswerHangupRedialState : : StartCall
2020-08-14 14:54:15 +04:00
: AnswerHangupRedialState : : Hangup ;
if ( _answerHangupRedialState ! = answerHangupRedialState ) {
_answerHangupRedialState = answerHangupRedialState ;
refreshAnswerHangupRedialLabel ( ) ;
}
2020-08-14 16:51:47 +04:00
if ( ! _call - > isKeyShaForFingerprintReady ( ) ) {
_fingerprint . destroy ( ) ;
} else if ( ! _fingerprint ) {
_fingerprint = CreateFingerprintAndSignalBars ( widget ( ) , _call ) ;
updateControlsGeometry ( ) ;
2017-05-04 16:32:56 +03:00
}
2017-05-04 15:29:32 +03:00
}
2017-04-25 19:45:41 +03:00
}
2020-08-14 14:54:15 +04:00
void Panel : : refreshAnswerHangupRedialLabel ( ) {
Expects ( _answerHangupRedialState . has_value ( ) ) ;
_answerHangupRedial - > setText ( [ & ] {
switch ( * _answerHangupRedialState ) {
case AnswerHangupRedialState : : Answer : return tr : : lng_call_accept ( ) ;
case AnswerHangupRedialState : : Hangup : return tr : : lng_call_end_call ( ) ;
case AnswerHangupRedialState : : Redial : return tr : : lng_call_redial ( ) ;
2023-02-27 18:30:50 +03:00
case AnswerHangupRedialState : : StartCall : return tr : : lng_call_start ( ) ;
2020-08-14 14:54:15 +04:00
}
Unexpected ( " AnswerHangupRedialState value. " ) ;
} ( ) ) ;
}
2017-04-25 19:45:41 +03:00
void Panel : : updateStatusText ( State state ) {
auto statusText = [ this , state ] ( ) - > QString {
switch ( state ) {
2017-04-25 23:36:04 +03:00
case State : : Starting :
2017-04-25 19:45:41 +03:00
case State : : WaitingInit :
2019-06-19 17:09:03 +02:00
case State : : WaitingInitAck : return tr : : lng_call_status_connecting ( tr : : now ) ;
2017-04-25 19:45:41 +03:00
case State : : Established : {
if ( _call ) {
auto durationMs = _call - > getDurationMs ( ) ;
auto durationSeconds = durationMs / 1000 ;
startDurationUpdateTimer ( durationMs ) ;
2020-09-30 14:32:02 +03:00
return Ui : : FormatDurationText ( durationSeconds ) ;
2017-04-25 19:45:41 +03:00
}
2019-06-19 17:09:03 +02:00
return tr : : lng_call_status_ended ( tr : : now ) ;
2017-04-25 19:45:41 +03:00
} break ;
2017-05-09 15:06:21 +03:00
case State : : FailedHangingUp :
2019-06-19 17:09:03 +02:00
case State : : Failed : return tr : : lng_call_status_failed ( tr : : now ) ;
case State : : HangingUp : return tr : : lng_call_status_hanging ( tr : : now ) ;
2017-05-12 19:09:34 +03:00
case State : : Ended :
2019-06-19 17:09:03 +02:00
case State : : EndedByOtherDevice : return tr : : lng_call_status_ended ( tr : : now ) ;
case State : : ExchangingKeys : return tr : : lng_call_status_exchanging ( tr : : now ) ;
case State : : Waiting : return tr : : lng_call_status_waiting ( tr : : now ) ;
case State : : Requesting : return tr : : lng_call_status_requesting ( tr : : now ) ;
case State : : WaitingIncoming : return tr : : lng_call_status_incoming ( tr : : now ) ;
case State : : Ringing : return tr : : lng_call_status_ringing ( tr : : now ) ;
case State : : Busy : return tr : : lng_call_status_busy ( tr : : now ) ;
2023-02-27 18:30:50 +03:00
case State : : WaitingUserConfirmation : return tr : : lng_call_status_sure ( tr : : now ) ;
2017-04-25 19:45:41 +03:00
}
Unexpected ( " State in stateChanged() " ) ;
} ;
_status - > setText ( statusText ( ) ) ;
updateStatusGeometry ( ) ;
}
2019-02-19 10:57:53 +04:00
void Panel : : startDurationUpdateTimer ( crl : : time currentDuration ) {
2017-04-25 19:45:41 +03:00
auto msTillNextSecond = 1000 - ( currentDuration % 1000 ) ;
_updateDurationTimer . callOnce ( msTillNextSecond + 5 ) ;
2017-04-19 23:25:48 +03:00
}
} // namespace Calls