| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "calls/group/calls_group_panel.h"
- #include "calls/group/calls_group_common.h"
- #include "calls/group/calls_group_members.h"
- #include "calls/group/calls_group_settings.h"
- #include "calls/group/calls_group_menu.h"
- #include "calls/group/calls_group_viewport.h"
- #include "calls/group/calls_group_toasts.h"
- #include "calls/group/calls_group_invite_controller.h"
- #include "calls/group/ui/calls_group_scheduled_labels.h"
- #include "calls/group/ui/desktop_capture_choose_source.h"
- #include "ui/platform/ui_platform_window_title.h"
- #include "ui/platform/ui_platform_utility.h"
- #include "ui/controls/call_mute_button.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/call_button.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/dropdown_menu.h"
- #include "ui/widgets/fields/input_field.h"
- #include "ui/widgets/tooltip.h"
- #include "ui/widgets/rp_window.h"
- #include "ui/chat/group_call_bar.h"
- #include "ui/controls/userpic_button.h"
- #include "ui/layers/layer_manager.h"
- #include "ui/layers/generic_box.h"
- #include "ui/text/text_utilities.h"
- #include "ui/toast/toast.h"
- #include "ui/image/image_prepare.h"
- #include "ui/integration.h"
- #include "ui/painter.h"
- #include "ui/round_rect.h"
- #include "info/profile/info_profile_values.h" // Info::Profile::Value.
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "lang/lang_keys.h"
- #include "data/data_channel.h"
- #include "data/data_chat.h"
- #include "data/data_user.h"
- #include "data/data_group_call.h"
- #include "data/data_session.h"
- #include "data/data_changes.h"
- #include "main/session/session_show.h"
- #include "main/main_session.h"
- #include "base/event_filter.h"
- #include "base/unixtime.h"
- #include "base/qt_signal_producer.h"
- #include "base/timer_rpl.h"
- #include "base/power_save_blocker.h"
- #include "apiwrap.h" // api().kick.
- #include "api/api_chat_participants.h" // api().kick.
- #include "webrtc/webrtc_environment.h"
- #include "webrtc/webrtc_video_track.h"
- #include "webrtc/webrtc_audio_input_tester.h"
- #include "styles/style_calls.h"
- #include "styles/style_layers.h"
- #include <QtWidgets/QApplication>
- #include <QtGui/QWindow>
- #include <QtGui/QScreen>
- namespace Calls::Group {
- namespace {
- constexpr auto kSpacePushToTalkDelay = crl::time(250);
- constexpr auto kRecordingAnimationDuration = crl::time(1200);
- constexpr auto kRecordingOpacity = 0.6;
- constexpr auto kStartNoConfirmation = TimeId(10);
- constexpr auto kControlsBackgroundOpacity = 0.8;
- constexpr auto kOverrideActiveColorBgAlpha = 172;
- constexpr auto kHideControlsTimeout = 5 * crl::time(1000);
- class Show final : public Main::SessionShow {
- public:
- explicit Show(not_null<Panel*> panel);
- ~Show();
- void showOrHideBoxOrLayer(
- std::variant<
- v::null_t,
- object_ptr<Ui::BoxContent>,
- std::unique_ptr<Ui::LayerWidget>> &&layer,
- Ui::LayerOptions options,
- anim::type animated) const override;
- [[nodiscard]] not_null<QWidget*> toastParent() const override;
- [[nodiscard]] bool valid() const override;
- operator bool() const override;
- [[nodiscard]] Main::Session &session() const override;
- private:
- const base::weak_ptr<Panel> _panel;
- };
- Show::Show(not_null<Panel*> panel)
- : _panel(base::make_weak(panel)) {
- }
- Show::~Show() = default;
- void Show::showOrHideBoxOrLayer(
- std::variant<
- v::null_t,
- object_ptr<Ui::BoxContent>,
- std::unique_ptr<Ui::LayerWidget>> &&layer,
- Ui::LayerOptions options,
- anim::type animated) const {
- using UniqueLayer = std::unique_ptr<Ui::LayerWidget>;
- using ObjectBox = object_ptr<Ui::BoxContent>;
- if (auto layerWidget = std::get_if<UniqueLayer>(&layer)) {
- if (const auto panel = _panel.get()) {
- panel->showLayer(std::move(*layerWidget), options, animated);
- }
- } else if (auto box = std::get_if<ObjectBox>(&layer)) {
- if (const auto panel = _panel.get()) {
- panel->showBox(std::move(*box), options, animated);
- }
- } else if (const auto panel = _panel.get()) {
- panel->hideLayer(animated);
- }
- }
- not_null<QWidget*> Show::toastParent() const {
- const auto panel = _panel.get();
- Assert(panel != nullptr);
- return panel->widget();
- }
- bool Show::valid() const {
- return !_panel.empty();
- }
- Show::operator bool() const {
- return valid();
- }
- Main::Session &Show::session() const {
- const auto panel = _panel.get();
- Assert(panel != nullptr);
- return panel->call()->peer()->session();
- }
- #ifdef Q_OS_WIN
- void UnpinMaximized(not_null<QWidget*> widget) {
- SetWindowPos(
- reinterpret_cast<HWND>(widget->window()->windowHandle()->winId()),
- HWND_NOTOPMOST,
- 0,
- 0,
- 0,
- 0,
- (SWP_NOMOVE
- | SWP_NOSIZE
- | SWP_NOOWNERZORDER
- | SWP_FRAMECHANGED
- | SWP_NOACTIVATE));
- }
- #endif // Q_OS_WIN
- } // namespace
- struct Panel::ControlsBackgroundNarrow {
- explicit ControlsBackgroundNarrow(not_null<QWidget*> parent)
- : shadow(parent)
- , blocker(parent) {
- }
- Ui::RpWidget shadow;
- Ui::RpWidget blocker;
- };
- Panel::Panel(not_null<GroupCall*> call)
- : _call(call)
- , _peer(call->peer())
- , _layerBg(std::make_unique<Ui::LayerManager>(widget()))
- #ifndef Q_OS_MAC
- , _controls(Ui::Platform::SetupSeparateTitleControls(
- window(),
- st::groupCallTitle,
- nullptr,
- _controlsTop.value()))
- #endif // !Q_OS_MAC
- , _powerSaveBlocker(std::make_unique<base::PowerSaveBlocker>(
- base::PowerSaveBlockType::PreventDisplaySleep,
- u"Video chat is active"_q,
- window()->windowHandle()))
- , _viewport(
- std::make_unique<Viewport>(widget(), PanelMode::Wide, _window.backend()))
- , _mute(std::make_unique<Ui::CallMuteButton>(
- widget(),
- st::callMuteButton,
- Core::App().appDeactivatedValue(),
- Ui::CallMuteButtonState{
- .text = (_call->scheduleDate()
- ? tr::lng_group_call_start_now(tr::now)
- : tr::lng_group_call_connecting(tr::now)),
- .type = (!_call->scheduleDate()
- ? Ui::CallMuteButtonType::Connecting
- : _peer->canManageGroupCall()
- ? Ui::CallMuteButtonType::ScheduledCanStart
- : _call->scheduleStartSubscribed()
- ? Ui::CallMuteButtonType::ScheduledNotify
- : Ui::CallMuteButtonType::ScheduledSilent),
- .expandType = ((_call->scheduleDate() || !_call->rtmp())
- ? Ui::CallMuteButtonExpandType::None
- : Ui::CallMuteButtonExpandType::Normal),
- }))
- , _hangup(widget(), st::groupCallHangup)
- , _stickedTooltipsShown(Core::App().settings().hiddenGroupCallTooltips()
- & ~StickedTooltip::Microphone) // Always show tooltip about mic.
- , _toasts(std::make_unique<Toasts>(this))
- , _controlsBackgroundColor([] {
- auto result = st::groupCallBg->c;
- result.setAlphaF(kControlsBackgroundOpacity);
- return result;
- })
- , _hideControlsTimer([=] { toggleWideControls(false); }) {
- _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
- _layerBg->setHideByBackgroundClick(true);
- _viewport->widget()->hide();
- if (!_viewport->requireARGB32()) {
- _call->setNotRequireARGB32();
- }
- SubscribeToMigration(
- _peer,
- lifetime(),
- [=](not_null<ChannelData*> channel) { migrate(channel); });
- setupRealCallViewers();
- initWindow();
- initWidget();
- initControls();
- initLayout();
- showAndActivate();
- }
- Panel::~Panel() {
- _menu.destroy();
- _viewport = nullptr;
- }
- void Panel::setupRealCallViewers() {
- _call->real(
- ) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
- subscribeToChanges(real);
- }, lifetime());
- }
- not_null<GroupCall*> Panel::call() const {
- return _call;
- }
- bool Panel::isVisible() const {
- return window()->isVisible()
- && !(window()->windowState() & Qt::WindowMinimized);
- }
- bool Panel::isActive() const {
- return window()->isActiveWindow() && isVisible();
- }
- base::weak_ptr<Ui::Toast::Instance> Panel::showToast(
- const QString &text,
- crl::time duration) {
- return Show(this).showToast(text, duration);
- }
- base::weak_ptr<Ui::Toast::Instance> Panel::showToast(
- TextWithEntities &&text,
- crl::time duration) {
- return Show(this).showToast(std::move(text), duration);
- }
- base::weak_ptr<Ui::Toast::Instance> Panel::showToast(
- Ui::Toast::Config &&config) {
- return Show(this).showToast(std::move(config));
- }
- std::shared_ptr<Main::SessionShow> Panel::uiShow() {
- return std::make_shared<Show>(this);
- }
- void Panel::minimize() {
- window()->setWindowState(window()->windowState() | Qt::WindowMinimized);
- }
- void Panel::close() {
- window()->close();
- }
- void Panel::showAndActivate() {
- if (window()->isHidden()) {
- window()->show();
- }
- const auto state = window()->windowState();
- if (state & Qt::WindowMinimized) {
- window()->setWindowState(state & ~Qt::WindowMinimized);
- }
- window()->raise();
- window()->activateWindow();
- window()->setFocus();
- }
- void Panel::migrate(not_null<ChannelData*> channel) {
- _peer = channel;
- _peerLifetime.destroy();
- subscribeToPeerChanges();
- _title.destroy();
- _titleSeparator.destroy();
- _viewers.destroy();
- refreshTitle();
- }
- void Panel::subscribeToPeerChanges() {
- Info::Profile::NameValue(
- _peer
- ) | rpl::start_with_next([=](const QString &name) {
- window()->setTitle(name);
- }, _peerLifetime);
- }
- QWidget *Panel::chooseSourceParent() {
- return window().get();
- }
- QString Panel::chooseSourceActiveDeviceId() {
- return _call->screenSharingDeviceId();
- }
- bool Panel::chooseSourceActiveWithAudio() {
- return _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();
- }
- void Panel::chooseSourceAccepted(
- const QString &deviceId,
- bool withAudio) {
- _call->toggleScreenSharing(deviceId, withAudio);
- }
- void Panel::chooseSourceStop() {
- _call->toggleScreenSharing(std::nullopt);
- }
- void Panel::initWindow() {
- window()->setAttribute(Qt::WA_OpaquePaintEvent);
- window()->setAttribute(Qt::WA_NoSystemBackground);
- window()->setTitleStyle(st::groupCallTitle);
- subscribeToPeerChanges();
- base::install_event_filter(window().get(), [=](not_null<QEvent*> e) {
- if (e->type() == QEvent::Close && handleClose()) {
- e->ignore();
- return base::EventFilterResult::Cancel;
- } else if (e->type() == QEvent::KeyPress
- || e->type() == QEvent::KeyRelease) {
- const auto key = static_cast<QKeyEvent*>(e.get())->key();
- if (key == Qt::Key_Space) {
- _call->pushToTalk(
- e->type() == QEvent::KeyPress,
- kSpacePushToTalkDelay);
- } else if (key == Qt::Key_Escape
- && _fullScreenOrMaximized.current()) {
- toggleFullScreen();
- }
- } else if (e->type() == QEvent::WindowStateChange && _call->rtmp()) {
- const auto state = window()->windowState();
- _fullScreenOrMaximized = (state & Qt::WindowFullScreen)
- || (state & Qt::WindowMaximized);
- }
- return base::EventFilterResult::Continue;
- });
- window()->setBodyTitleArea([=](QPoint widgetPoint) {
- using Flag = Ui::WindowTitleHitTestFlag;
- const auto titleRect = QRect(
- 0,
- 0,
- widget()->width(),
- (mode() == PanelMode::Wide
- ? st::groupCallWideVideoTop
- : st::groupCallMembersTop));
- const auto moveable = (titleRect.contains(widgetPoint)
- && (!_menuToggle || !_menuToggle->geometry().contains(widgetPoint))
- && (!_menu || !_menu->geometry().contains(widgetPoint))
- && (!_recordingMark || !_recordingMark->geometry().contains(widgetPoint))
- && (!_joinAsToggle || !_joinAsToggle->geometry().contains(widgetPoint)));
- if (!moveable) {
- return (Flag::None | Flag(0));
- }
- const auto shown = _layerBg->topShownLayer();
- return (!shown || !shown->geometry().contains(widgetPoint))
- ? (Flag::Move | Flag::Menu | Flag::Maximize)
- : Flag::None;
- });
- _call->hasVideoWithFramesValue(
- ) | rpl::start_with_next([=] {
- updateMode();
- }, lifetime());
- }
- void Panel::initWidget() {
- widget()->setMouseTracking(true);
- widget()->paintRequest(
- ) | rpl::start_with_next([=](QRect clip) {
- paint(clip);
- }, lifetime());
- widget()->sizeValue(
- ) | rpl::skip(1) | rpl::start_with_next([=](QSize size) {
- if (!updateMode()) {
- updateControlsGeometry();
- }
- // some geometries depends on _controls->controls.geometry,
- // which is not updated here yet.
- crl::on_main(widget(), [=] { updateControlsGeometry(); });
- }, lifetime());
- }
- void Panel::endCall() {
- if (!_call->canManage()) {
- _call->hangup();
- return;
- }
- showBox(Box(
- LeaveBox,
- _call,
- false,
- BoxContext::GroupCallPanel));
- }
- void Panel::startScheduledNow() {
- const auto date = _call->scheduleDate();
- const auto now = base::unixtime::now();
- if (!date) {
- return;
- } else if (now + kStartNoConfirmation >= date) {
- _call->startScheduledNow();
- } else {
- const auto box = std::make_shared<QPointer<Ui::GenericBox>>();
- const auto done = [=] {
- if (*box) {
- (*box)->closeBox();
- }
- _call->startScheduledNow();
- };
- auto owned = ConfirmBox({
- .text = (_call->peer()->isBroadcast()
- ? tr::lng_group_call_start_now_sure_channel
- : tr::lng_group_call_start_now_sure)(),
- .confirmed = done,
- .confirmText = tr::lng_group_call_start_now(),
- });
- *box = owned.data();
- showBox(std::move(owned));
- }
- }
- void Panel::initControls() {
- _mute->clicks(
- ) | rpl::filter([=](Qt::MouseButton button) {
- return (button == Qt::LeftButton);
- }) | rpl::start_with_next([=] {
- if (_call->scheduleDate()) {
- if (_call->canManage()) {
- startScheduledNow();
- } else if (const auto real = _call->lookupReal()) {
- _call->toggleScheduleStartSubscribed(
- !real->scheduleStartSubscribed());
- }
- return;
- } else if (_call->rtmp()) {
- toggleFullScreen();
- return;
- }
- const auto oldState = _call->muted();
- const auto newState = (oldState == MuteState::ForceMuted)
- ? MuteState::RaisedHand
- : (oldState == MuteState::RaisedHand)
- ? MuteState::RaisedHand
- : (oldState == MuteState::Muted)
- ? MuteState::Active
- : MuteState::Muted;
- _call->setMutedAndUpdate(newState);
- }, _mute->lifetime());
- initShareAction();
- refreshLeftButton();
- refreshVideoButtons();
- rpl::combine(
- _mode.value(),
- _call->canManageValue()
- ) | rpl::start_with_next([=] {
- refreshTopButton();
- }, lifetime());
- _hangup->setClickedCallback([=] { endCall(); });
- const auto scheduleDate = _call->scheduleDate();
- if (scheduleDate) {
- auto changes = _call->real(
- ) | rpl::map([=](not_null<Data::GroupCall*> real) {
- return real->scheduleDateValue();
- }) | rpl::flatten_latest();
- setupScheduledLabels(rpl::single(
- scheduleDate
- ) | rpl::then(rpl::duplicate(changes)));
- auto started = std::move(changes) | rpl::filter([](TimeId date) {
- return (date == 0);
- }) | rpl::take(1);
- rpl::merge(
- rpl::duplicate(started) | rpl::to_empty,
- _peer->session().changes().peerFlagsValue(
- _peer,
- Data::PeerUpdate::Flag::Username
- ) | rpl::skip(1) | rpl::to_empty
- ) | rpl::start_with_next([=] {
- refreshLeftButton();
- updateControlsGeometry();
- }, _callLifetime);
- std::move(started) | rpl::start_with_next([=] {
- refreshVideoButtons();
- updateButtonsStyles();
- setupMembers();
- }, _callLifetime);
- }
- _call->stateValue(
- ) | rpl::before_next([=] {
- showStickedTooltip();
- }) | rpl::filter([](State state) {
- return (state == State::HangingUp)
- || (state == State::Ended)
- || (state == State::FailedHangingUp)
- || (state == State::Failed);
- }) | rpl::start_with_next([=] {
- closeBeforeDestroy();
- }, _callLifetime);
- _call->levelUpdates(
- ) | rpl::filter([=](const LevelUpdate &update) {
- return update.me;
- }) | rpl::start_with_next([=](const LevelUpdate &update) {
- _mute->setLevel(update.value);
- }, _callLifetime);
- _call->real(
- ) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
- setupRealMuteButtonState(real);
- }, _callLifetime);
- refreshControlsBackground();
- }
- void Panel::toggleFullScreen() {
- if (_fullScreenOrMaximized.current() || window()->isFullScreen()) {
- window()->showNormal();
- } else {
- window()->showFullScreen();
- }
- }
- void Panel::refreshLeftButton() {
- const auto share = _call->scheduleDate()
- && _peer->isBroadcast()
- && _peer->asChannel()->hasUsername();
- if ((share && _callShare) || (!share && _settings)) {
- return;
- }
- if (share) {
- _settings.destroy();
- _callShare.create(widget(), st::groupCallShare);
- _callShare->setClickedCallback(_callShareLinkCallback);
- } else {
- _callShare.destroy();
- _settings.create(widget(), st::groupCallSettings);
- _settings->setClickedCallback([=] {
- showBox(Box(SettingsBox, _call));
- });
- trackControls(_trackControls, true);
- }
- const auto raw = _callShare ? _callShare.data() : _settings.data();
- raw->show();
- raw->setColorOverrides(_mute->colorOverrides());
- updateButtonsStyles();
- }
- void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
- const auto create = overrideWideMode.value_or(mode() == PanelMode::Wide)
- || (!_call->scheduleDate() && _call->videoIsWorking());
- const auto created = _video && _screenShare;
- if (created == create) {
- return;
- } else if (created) {
- _video.destroy();
- _screenShare.destroy();
- if (!overrideWideMode) {
- updateButtonsGeometry();
- }
- return;
- }
- auto toggleableOverrides = [&](rpl::producer<bool> active) {
- return rpl::combine(
- std::move(active),
- _mute->colorOverrides()
- ) | rpl::map([](bool active, Ui::CallButtonColors colors) {
- if (active && colors.bg) {
- colors.bg->setAlpha(kOverrideActiveColorBgAlpha);
- }
- return colors;
- });
- };
- if (!_video) {
- _video.create(
- widget(),
- st::groupCallVideoSmall,
- &st::groupCallVideoActiveSmall);
- _video->show();
- _video->setClickedCallback([=] {
- hideStickedTooltip(
- StickedTooltip::Camera,
- StickedTooltipHide::Activated);
- _call->toggleVideo(!_call->isSharingCamera());
- });
- _video->setColorOverrides(
- toggleableOverrides(_call->isSharingCameraValue()));
- _call->isSharingCameraValue(
- ) | rpl::start_with_next([=](bool sharing) {
- if (sharing) {
- hideStickedTooltip(
- StickedTooltip::Camera,
- StickedTooltipHide::Activated);
- }
- _video->setProgress(sharing ? 1. : 0.);
- }, _video->lifetime());
- }
- if (!_screenShare) {
- _screenShare.create(widget(), st::groupCallScreenShareSmall);
- _screenShare->show();
- _screenShare->setClickedCallback([=] {
- chooseShareScreenSource();
- });
- _screenShare->setColorOverrides(
- toggleableOverrides(_call->isSharingScreenValue()));
- _call->isSharingScreenValue(
- ) | rpl::start_with_next([=](bool sharing) {
- _screenShare->setProgress(sharing ? 1. : 0.);
- }, _screenShare->lifetime());
- }
- if (!_wideMenu) {
- _wideMenu.create(widget(), st::groupCallMenuToggleSmall);
- _wideMenu->show();
- _wideMenu->setClickedCallback([=] { showMainMenu(); });
- _wideMenu->setColorOverrides(
- toggleableOverrides(_wideMenuShown.value()));
- }
- updateButtonsStyles();
- updateButtonsGeometry();
- raiseControls();
- }
- void Panel::hideStickedTooltip(StickedTooltipHide hide) {
- if (!_stickedTooltipClose || !_niceTooltipControl) {
- return;
- }
- if (_niceTooltipControl.data() == _video.data()) {
- hideStickedTooltip(StickedTooltip::Camera, hide);
- } else if (_niceTooltipControl.data() == _mute->outer().get()) {
- hideStickedTooltip(StickedTooltip::Microphone, hide);
- }
- }
- void Panel::hideStickedTooltip(
- StickedTooltip type,
- StickedTooltipHide hide) {
- if (hide != StickedTooltipHide::Unavailable) {
- _stickedTooltipsShown |= type;
- if (hide == StickedTooltipHide::Discarded) {
- Core::App().settings().setHiddenGroupCallTooltip(type);
- Core::App().saveSettingsDelayed();
- }
- }
- const auto control = (type == StickedTooltip::Camera)
- ? _video.data()
- : (type == StickedTooltip::Microphone)
- ? _mute->outer().get()
- : nullptr;
- if (_niceTooltipControl.data() == control) {
- hideNiceTooltip();
- }
- }
- void Panel::hideNiceTooltip() {
- if (!_niceTooltip) {
- return;
- }
- _stickedTooltipClose = nullptr;
- _niceTooltip.release()->toggleAnimated(false);
- _niceTooltipControl = nullptr;
- }
- void Panel::initShareAction() {
- auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction(
- _peer,
- uiShow());
- _callShareLinkCallback = [=, callback = std::move(shareLinkCallback)] {
- if (_call->lookupReal()) {
- callback();
- }
- };
- lifetime().add(std::move(shareLinkLifetime));
- }
- void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
- using namespace rpl::mappers;
- rpl::combine(
- _call->mutedValue() | MapPushToTalkToActive(),
- _call->instanceStateValue(),
- real->scheduleDateValue(),
- real->scheduleStartSubscribedValue(),
- _call->canManageValue(),
- _mode.value(),
- _fullScreenOrMaximized.value()
- ) | rpl::distinct_until_changed(
- ) | rpl::filter(
- _2 != GroupCall::InstanceState::TransitionToRtc
- ) | rpl::start_with_next([=](
- MuteState mute,
- GroupCall::InstanceState state,
- TimeId scheduleDate,
- bool scheduleStartSubscribed,
- bool canManage,
- PanelMode mode,
- bool fullScreenOrMaximized) {
- const auto wide = (mode == PanelMode::Wide);
- using Type = Ui::CallMuteButtonType;
- using ExpandType = Ui::CallMuteButtonExpandType;
- _mute->setState(Ui::CallMuteButtonState{
- .text = (wide
- ? QString()
- : scheduleDate
- ? (canManage
- ? tr::lng_group_call_start_now(tr::now)
- : scheduleStartSubscribed
- ? tr::lng_group_call_cancel_reminder(tr::now)
- : tr::lng_group_call_set_reminder(tr::now))
- : state == GroupCall::InstanceState::Disconnected
- ? tr::lng_group_call_connecting(tr::now)
- : mute == MuteState::ForceMuted
- ? tr::lng_group_call_force_muted(tr::now)
- : mute == MuteState::RaisedHand
- ? tr::lng_group_call_raised_hand(tr::now)
- : mute == MuteState::Muted
- ? tr::lng_group_call_unmute(tr::now)
- : tr::lng_group_call_you_are_live(tr::now)),
- .tooltip = ((!scheduleDate && mute == MuteState::Muted)
- ? tr::lng_group_call_unmute_sub(tr::now)
- : QString()),
- .type = (scheduleDate
- ? (canManage
- ? Type::ScheduledCanStart
- : scheduleStartSubscribed
- ? Type::ScheduledNotify
- : Type::ScheduledSilent)
- : state == GroupCall::InstanceState::Disconnected
- ? Type::Connecting
- : mute == MuteState::ForceMuted
- ? Type::ForceMuted
- : mute == MuteState::RaisedHand
- ? Type::RaisedHand
- : mute == MuteState::Muted
- ? Type::Muted
- : Type::Active),
- .expandType = ((scheduleDate || !_call->rtmp())
- ? ExpandType::None
- : fullScreenOrMaximized
- ? ExpandType::Expanded
- : ExpandType::Normal),
- });
- }, _callLifetime);
- }
- void Panel::setupScheduledLabels(rpl::producer<TimeId> date) {
- using namespace rpl::mappers;
- date = std::move(date) | rpl::take_while(_1 != 0);
- _startsWhen.create(
- widget(),
- Ui::StartsWhenText(rpl::duplicate(date)),
- st::groupCallStartsWhen);
- auto countdownCreated = std::move(
- date
- ) | rpl::map([=](TimeId date) {
- _countdownData = std::make_shared<Ui::GroupCallScheduledLeft>(date);
- return rpl::empty;
- }) | rpl::start_spawning(lifetime());
- _countdown = Ui::CreateGradientLabel(widget(), rpl::duplicate(
- countdownCreated
- ) | rpl::map([=] {
- return _countdownData->text(
- Ui::GroupCallScheduledLeft::Negative::Ignore);
- }) | rpl::flatten_latest());
- _startsIn.create(
- widget(),
- rpl::conditional(
- std::move(
- countdownCreated
- ) | rpl::map(
- [=] { return _countdownData->late(); }
- ) | rpl::flatten_latest(),
- tr::lng_group_call_late_by(),
- tr::lng_group_call_starts_in()),
- st::groupCallStartsIn);
- const auto top = [=] {
- const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
- const auto membersTop = st::groupCallMembersTop;
- const auto height = st::groupCallScheduledBodyHeight;
- return (membersTop + (muteTop - membersTop - height) / 2);
- };
- rpl::combine(
- widget()->sizeValue(),
- _startsIn->widthValue()
- ) | rpl::start_with_next([=](QSize size, int width) {
- _startsIn->move(
- (size.width() - width) / 2,
- top() + st::groupCallStartsInTop);
- }, _startsIn->lifetime());
- rpl::combine(
- widget()->sizeValue(),
- _startsWhen->widthValue()
- ) | rpl::start_with_next([=](QSize size, int width) {
- _startsWhen->move(
- (size.width() - width) / 2,
- top() + st::groupCallStartsWhenTop);
- }, _startsWhen->lifetime());
- rpl::combine(
- widget()->sizeValue(),
- _countdown->widthValue()
- ) | rpl::start_with_next([=](QSize size, int width) {
- _countdown->move(
- (size.width() - width) / 2,
- top() + st::groupCallCountdownTop);
- }, _startsWhen->lifetime());
- }
- PanelMode Panel::mode() const {
- return _mode.current();
- }
- void Panel::setupMembers() {
- if (_members) {
- return;
- }
- _startsIn.destroy();
- _countdown.destroy();
- _startsWhen.destroy();
- _members.create(widget(), _call, mode(), _window.backend());
- setupVideo(_viewport.get());
- setupVideo(_members->viewport());
- _viewport->mouseInsideValue(
- ) | rpl::filter([=] {
- return !_fullScreenOrMaximized.current();
- }) | rpl::start_with_next([=](bool inside) {
- toggleWideControls(inside);
- }, _viewport->lifetime());
- _members->show();
- setupEmptyRtmp();
- refreshControlsBackground();
- raiseControls();
- _members->desiredHeightValue(
- ) | rpl::start_with_next([=] {
- updateMembersGeometry();
- }, _members->lifetime());
- _members->toggleMuteRequests(
- ) | rpl::start_with_next([=](MuteRequest request) {
- if (_call) {
- _call->toggleMute(request);
- }
- }, _callLifetime);
- _members->changeVolumeRequests(
- ) | rpl::start_with_next([=](VolumeRequest request) {
- if (_call) {
- _call->changeVolume(request);
- }
- }, _callLifetime);
- _members->kickParticipantRequests(
- ) | rpl::start_with_next([=](not_null<PeerData*> participantPeer) {
- kickParticipant(participantPeer);
- }, _callLifetime);
- _members->addMembersRequests(
- ) | rpl::start_with_next([=] {
- if (!_peer->isBroadcast()
- && Data::CanSend(_peer, ChatRestriction::SendOther, false)
- && _call->joinAs()->isSelf()) {
- addMembers();
- } else if (const auto channel = _peer->asChannel()) {
- if (channel->hasUsername()) {
- _callShareLinkCallback();
- }
- }
- }, _callLifetime);
- _call->videoEndpointLargeValue(
- ) | rpl::start_with_next([=](const VideoEndpoint &large) {
- if (large && mode() != PanelMode::Wide) {
- enlargeVideo();
- }
- _viewport->showLarge(large);
- }, _callLifetime);
- }
- void Panel::enlargeVideo() {
- _lastSmallGeometry = window()->geometry();
- const auto available = window()->screen()->availableGeometry();
- const auto width = std::max(
- window()->width(),
- std::max(
- std::min(available.width(), st::groupCallWideModeSize.width()),
- st::groupCallWideModeWidthMin));
- const auto height = std::max(
- window()->height(),
- std::min(available.height(), st::groupCallWideModeSize.height()));
- auto geometry = QRect(window()->pos(), QSize(width, height));
- if (geometry.x() < available.x()) {
- geometry.moveLeft(std::min(available.x(), window()->x()));
- }
- if (geometry.x() + geometry.width()
- > available.x() + available.width()) {
- geometry.moveLeft(std::max(
- available.x() + available.width(),
- window()->x() + window()->width()) - geometry.width());
- }
- if (geometry.y() < available.y()) {
- geometry.moveTop(std::min(available.y(), window()->y()));
- }
- if (geometry.y() + geometry.height() > available.y() + available.height()) {
- geometry.moveTop(std::max(
- available.y() + available.height(),
- window()->y() + window()->height()) - geometry.height());
- }
- if (_lastLargeMaximized) {
- window()->setWindowState(
- window()->windowState() | Qt::WindowMaximized);
- } else {
- window()->setGeometry((_lastLargeGeometry
- && available.intersects(*_lastLargeGeometry))
- ? *_lastLargeGeometry
- : geometry);
- }
- }
- void Panel::raiseControls() {
- if (_controlsBackgroundWide) {
- _controlsBackgroundWide->raise();
- }
- if (_controlsBackgroundNarrow) {
- _controlsBackgroundNarrow->shadow.raise();
- _controlsBackgroundNarrow->blocker.raise();
- }
- const auto buttons = {
- &_settings,
- &_callShare,
- &_screenShare,
- &_wideMenu,
- &_video,
- &_hangup
- };
- for (const auto button : buttons) {
- if (const auto raw = button->data()) {
- raw->raise();
- }
- }
- _mute->raise();
- if (_titleBackground) {
- _titleBackground->raise();
- }
- if (_title) {
- _title->raise();
- }
- if (_viewers) {
- _titleSeparator->raise();
- _viewers->raise();
- }
- if (_menuToggle) {
- _menuToggle->raise();
- }
- if (_recordingMark) {
- _recordingMark->raise();
- }
- if (_pinOnTop) {
- _pinOnTop->raise();
- }
- _layerBg->raise();
- if (_niceTooltip) {
- _niceTooltip->raise();
- }
- }
- void Panel::setupVideo(not_null<Viewport*> viewport) {
- const auto setupTile = [=](
- const VideoEndpoint &endpoint,
- const std::unique_ptr<GroupCall::VideoTrack> &track) {
- using namespace rpl::mappers;
- const auto row = endpoint.rtmp()
- ? _members->rtmpFakeRow(GroupCall::TrackPeer(track)).get()
- : _members->lookupRow(GroupCall::TrackPeer(track));
- Assert(row != nullptr);
- auto pinned = rpl::combine(
- _call->videoEndpointLargeValue(),
- _call->videoEndpointPinnedValue()
- ) | rpl::map(_1 == endpoint && _2);
- const auto self = (endpoint.peer == _call->joinAs());
- viewport->add(
- endpoint,
- VideoTileTrack{ GroupCall::TrackPointer(track), row },
- GroupCall::TrackSizeValue(track),
- std::move(pinned),
- self);
- };
- for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
- setupTile(endpoint, track);
- }
- _call->videoStreamActiveUpdates(
- ) | rpl::start_with_next([=](const VideoStateToggle &update) {
- if (update.value) {
- // Add async (=> the participant row is definitely in Members).
- const auto endpoint = update.endpoint;
- crl::on_main(viewport->widget(), [=] {
- const auto &tracks = _call->activeVideoTracks();
- const auto i = tracks.find(endpoint);
- if (i != end(tracks)) {
- setupTile(endpoint, i->second);
- }
- });
- } else {
- // Remove sync.
- viewport->remove(update.endpoint);
- }
- }, viewport->lifetime());
- viewport->pinToggled(
- ) | rpl::start_with_next([=](bool pinned) {
- _call->pinVideoEndpoint(pinned
- ? _call->videoEndpointLarge()
- : VideoEndpoint{});
- }, viewport->lifetime());
- viewport->clicks(
- ) | rpl::start_with_next([=](VideoEndpoint &&endpoint) {
- if (_call->videoEndpointLarge() == endpoint) {
- _call->showVideoEndpointLarge({});
- } else if (_call->videoEndpointPinned()) {
- _call->pinVideoEndpoint(std::move(endpoint));
- } else {
- _call->showVideoEndpointLarge(std::move(endpoint));
- }
- }, viewport->lifetime());
- viewport->qualityRequests(
- ) | rpl::start_with_next([=](const VideoQualityRequest &request) {
- _call->requestVideoQuality(request.endpoint, request.quality);
- }, viewport->lifetime());
- }
- void Panel::toggleWideControls(bool shown) {
- if (_showWideControls == shown) {
- return;
- }
- _showWideControls = shown;
- crl::on_main(widget(), [=] {
- updateWideControlsVisibility();
- });
- }
- void Panel::updateWideControlsVisibility() {
- const auto shown = _showWideControls
- || (_stickedTooltipClose != nullptr);
- if (_wideControlsShown == shown) {
- return;
- }
- _viewport->setCursorShown(!_fullScreenOrMaximized.current() || shown);
- _wideControlsShown = shown;
- _wideControlsAnimation.start(
- [=] { updateButtonsGeometry(); },
- _wideControlsShown ? 0. : 1.,
- _wideControlsShown ? 1. : 0.,
- st::slideWrapDuration);
- }
- void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
- const auto livestream = real->peer()->isBroadcast();
- const auto validateRecordingMark = [=](bool recording) {
- if (!recording && _recordingMark) {
- _recordingMark.destroy();
- } else if (recording && !_recordingMark) {
- struct State {
- Ui::Animations::Simple animation;
- base::Timer timer;
- bool opaque = true;
- };
- _recordingMark.create(widget());
- _recordingMark->show();
- const auto state = _recordingMark->lifetime().make_state<State>();
- const auto size = st::groupCallRecordingMark;
- const auto skip = st::groupCallRecordingMarkSkip;
- _recordingMark->resize(size + 2 * skip, size + 2 * skip);
- _recordingMark->setClickedCallback([=] {
- showToast({ (livestream
- ? tr::lng_group_call_is_recorded_channel
- : real->recordVideo()
- ? tr::lng_group_call_is_recorded_video
- : tr::lng_group_call_is_recorded)(tr::now) });
- });
- const auto animate = [=] {
- const auto opaque = state->opaque;
- state->opaque = !opaque;
- state->animation.start(
- [=] { _recordingMark->update(); },
- opaque ? 1. : kRecordingOpacity,
- opaque ? kRecordingOpacity : 1.,
- kRecordingAnimationDuration);
- };
- state->timer.setCallback(animate);
- state->timer.callEach(kRecordingAnimationDuration);
- animate();
- _recordingMark->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = QPainter(_recordingMark.data());
- auto hq = PainterHighQualityEnabler(p);
- p.setPen(Qt::NoPen);
- p.setBrush(st::groupCallMemberMutedIcon);
- p.setOpacity(state->animation.value(
- state->opaque ? 1. : kRecordingOpacity));
- p.drawEllipse(skip, skip, size, size);
- }, _recordingMark->lifetime());
- }
- refreshTitleGeometry();
- };
- using namespace rpl::mappers;
- const auto startedAsVideo = std::make_shared<bool>(real->recordVideo());
- real->recordStartDateChanges(
- ) | rpl::map(
- _1 != 0
- ) | rpl::distinct_until_changed(
- ) | rpl::start_with_next([=](bool recorded) {
- const auto livestream = _call->peer()->isBroadcast();
- const auto isVideo = real->recordVideo();
- if (recorded) {
- *startedAsVideo = isVideo;
- }
- validateRecordingMark(recorded);
- showToast((recorded
- ? (livestream
- ? tr::lng_group_call_recording_started_channel
- : isVideo
- ? tr::lng_group_call_recording_started_video
- : tr::lng_group_call_recording_started)
- : _call->recordingStoppedByMe()
- ? ((*startedAsVideo)
- ? tr::lng_group_call_recording_saved_video
- : tr::lng_group_call_recording_saved)
- : (livestream
- ? tr::lng_group_call_recording_stopped_channel
- : tr::lng_group_call_recording_stopped))(
- tr::now,
- Ui::Text::RichLangValue));
- }, lifetime());
- validateRecordingMark(real->recordStartDate() != 0);
- rpl::combine(
- _call->videoIsWorkingValue(),
- _call->isSharingCameraValue()
- ) | rpl::start_with_next([=] {
- refreshVideoButtons();
- showStickedTooltip();
- }, lifetime());
- rpl::combine(
- _call->videoIsWorkingValue(),
- _call->isSharingScreenValue()
- ) | rpl::start_with_next([=] {
- refreshTopButton();
- }, lifetime());
- _call->mutedValue(
- ) | rpl::skip(1) | rpl::start_with_next([=](MuteState state) {
- updateButtonsGeometry();
- if (state == MuteState::Active
- || state == MuteState::PushToTalk) {
- hideStickedTooltip(
- StickedTooltip::Microphone,
- StickedTooltipHide::Activated);
- }
- showStickedTooltip();
- }, lifetime());
- updateControlsGeometry();
- }
- void Panel::createPinOnTop() {
- _pinOnTop.create(widget(), st::groupCallPinOnTop);
- const auto pinned = [=] {
- const auto handle = window()->windowHandle();
- return handle && (handle->flags() & Qt::WindowStaysOnTopHint);
- };
- const auto pin = [=](bool pin) {
- if (const auto handle = window()->windowHandle()) {
- handle->setFlag(Qt::WindowStaysOnTopHint, pin);
- _pinOnTop->setIconOverride(
- pin ? &st::groupCallPinnedOnTop : nullptr,
- pin ? &st::groupCallPinnedOnTop : nullptr);
- if (!_pinOnTop->isHidden()) {
- showToast({ pin
- ? tr::lng_group_call_pinned_on_top(tr::now)
- : tr::lng_group_call_unpinned_on_top(tr::now) });
- }
- }
- };
- _fullScreenOrMaximized.value(
- ) | rpl::start_with_next([=](bool fullScreenOrMaximized) {
- #ifndef Q_OS_MAC
- _controls->controls.setStyle(fullScreenOrMaximized
- ? st::callTitle
- : st::groupCallTitle);
- #endif // Q_OS_MAC
- _pinOnTop->setVisible(!fullScreenOrMaximized);
- if (fullScreenOrMaximized) {
- #ifdef Q_OS_WIN
- UnpinMaximized(window());
- _unpinnedMaximized = true;
- #else // Q_OS_WIN
- pin(false);
- #endif // Q_OS_WIN
- _viewport->rp()->events(
- ) | rpl::filter([](not_null<QEvent*> event) {
- return (event->type() == QEvent::MouseMove);
- }) | rpl::start_with_next([=] {
- _hideControlsTimer.callOnce(kHideControlsTimeout);
- toggleWideControls(true);
- }, _hideControlsTimerLifetime);
- _hideControlsTimer.callOnce(kHideControlsTimeout);
- } else {
- if (_unpinnedMaximized) {
- pin(false);
- }
- _hideControlsTimerLifetime.destroy();
- _hideControlsTimer.cancel();
- refreshTitleGeometry();
- }
- refreshTitleBackground();
- updateMembersGeometry();
- }, _pinOnTop->lifetime());
- _pinOnTop->setClickedCallback([=] {
- pin(!pinned());
- });
- updateControlsGeometry();
- }
- void Panel::refreshTopButton() {
- if (_call->rtmp() && !_pinOnTop) {
- createPinOnTop();
- }
- if (_mode.current() == PanelMode::Wide) {
- _menuToggle.destroy();
- _joinAsToggle.destroy();
- updateButtonsGeometry(); // _wideMenu <-> _settings
- return;
- }
- const auto hasJoinAs = _call->showChooseJoinAs();
- const auto showNarrowMenu = _call->canManage()
- || _call->videoIsWorking();
- const auto showNarrowUserpic = !showNarrowMenu && hasJoinAs;
- if (showNarrowMenu) {
- _joinAsToggle.destroy();
- if (!_menuToggle) {
- _menuToggle.create(widget(), st::groupCallMenuToggle);
- _menuToggle->show();
- _menuToggle->setClickedCallback([=] { showMainMenu(); });
- updateControlsGeometry();
- raiseControls();
- }
- } else if (showNarrowUserpic) {
- _menuToggle.destroy();
- rpl::single(
- _call->joinAs()
- ) | rpl::then(_call->rejoinEvents(
- ) | rpl::map([](const RejoinEvent &event) {
- return event.nowJoinAs;
- })) | rpl::start_with_next([=](not_null<PeerData*> joinAs) {
- auto joinAsToggle = object_ptr<Ui::UserpicButton>(
- widget(),
- joinAs,
- st::groupCallJoinAsToggle);
- _joinAsToggle.destroy();
- _joinAsToggle = std::move(joinAsToggle);
- _joinAsToggle->show();
- _joinAsToggle->setClickedCallback([=] {
- chooseJoinAs();
- });
- updateControlsGeometry();
- }, lifetime());
- } else {
- _menuToggle.destroy();
- _joinAsToggle.destroy();
- }
- }
- void Panel::screenSharingPrivacyRequest() {
- if (auto box = ScreenSharingPrivacyRequestBox()) {
- showBox(std::move(box));
- }
- }
- void Panel::chooseShareScreenSource() {
- if (_call->emitShareScreenError()) {
- return;
- }
- const auto choose = [=] {
- const auto env = &Core::App().mediaDevices();
- if (!env->desktopCaptureAllowed()) {
- screenSharingPrivacyRequest();
- } else if (const auto source = env->uniqueDesktopCaptureSource()) {
- if (_call->isSharingScreen()) {
- _call->toggleScreenSharing(std::nullopt);
- } else {
- chooseSourceAccepted(*source, false);
- }
- } else {
- Ui::DesktopCapture::ChooseSource(this);
- }
- };
- const auto screencastFromPeer = [&]() -> PeerData* {
- for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
- if (endpoint.type == VideoEndpointType::Screen) {
- return endpoint.peer;
- }
- }
- return nullptr;
- }();
- if (!screencastFromPeer || _call->isSharingScreen()) {
- choose();
- return;
- }
- const auto text = tr::lng_group_call_sure_screencast(
- tr::now,
- lt_user,
- screencastFromPeer->shortName());
- const auto shared = std::make_shared<QPointer<Ui::GenericBox>>();
- const auto done = [=] {
- if (*shared) {
- base::take(*shared)->closeBox();
- }
- choose();
- };
- auto box = ConfirmBox({
- .text = text,
- .confirmed = done,
- .confirmText = tr::lng_continue(),
- });
- *shared = box.data();
- showBox(std::move(box));
- }
- void Panel::chooseJoinAs() {
- const auto context = ChooseJoinAsProcess::Context::Switch;
- const auto callback = [=](JoinInfo info) {
- _call->rejoinAs(info);
- };
- _joinAsProcess.start(
- _peer,
- context,
- std::make_shared<Show>(this),
- callback,
- _call->joinAs());
- }
- void Panel::showMainMenu() {
- if (_menu) {
- return;
- }
- const auto wide = (_mode.current() == PanelMode::Wide) && _wideMenu;
- if (!wide && !_menuToggle) {
- return;
- }
- _menu.create(widget(), st::groupCallDropdownMenu);
- FillMenu(
- _menu.data(),
- _peer,
- _call,
- wide,
- [=] { chooseJoinAs(); },
- [=] { chooseShareScreenSource(); },
- [=](auto box) { showBox(std::move(box)); });
- if (_menu->empty()) {
- _wideMenuShown = false;
- _menu.destroy();
- return;
- }
- const auto raw = _menu.data();
- raw->setHiddenCallback([=] {
- raw->deleteLater();
- if (_menu == raw) {
- _menu = nullptr;
- _wideMenuShown = false;
- _trackControlsMenuLifetime.destroy();
- if (_menuToggle) {
- _menuToggle->setForceRippled(false);
- }
- }
- });
- raw->setShowStartCallback([=] {
- if (_menu == raw) {
- if (wide) {
- _wideMenuShown = true;
- } else if (_menuToggle) {
- _menuToggle->setForceRippled(true);
- }
- }
- });
- raw->setHideStartCallback([=] {
- if (_menu == raw) {
- _wideMenuShown = false;
- if (_menuToggle) {
- _menuToggle->setForceRippled(false);
- }
- }
- });
- if (wide) {
- _wideMenu->installEventFilter(_menu);
- trackControl(_menu, _trackControlsMenuLifetime);
- const auto x = st::groupCallWideMenuPosition.x();
- const auto y = st::groupCallWideMenuPosition.y();
- _menu->moveToLeft(
- _wideMenu->x() + x,
- _wideMenu->y() - _menu->height() + y);
- _menu->showAnimated(Ui::PanelAnimation::Origin::BottomLeft);
- } else {
- _menuToggle->installEventFilter(_menu);
- const auto x = st::groupCallMenuPosition.x();
- const auto y = st::groupCallMenuPosition.y();
- if (_menuToggle->x() > widget()->width() / 2) {
- _menu->moveToRight(x, y);
- _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
- } else {
- _menu->moveToLeft(x, y);
- _menu->showAnimated(Ui::PanelAnimation::Origin::TopLeft);
- }
- }
- }
- void Panel::addMembers() {
- const auto showToastCallback = [=](TextWithEntities &&text) {
- showToast(std::move(text));
- };
- if (auto box = PrepareInviteBox(_call, showToastCallback)) {
- showBox(std::move(box));
- }
- }
- void Panel::kickParticipant(not_null<PeerData*> participantPeer) {
- showBox(Box([=](not_null<Ui::GenericBox*> box) {
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box.get(),
- (!participantPeer->isUser()
- ? (_peer->isBroadcast()
- ? tr::lng_group_call_remove_channel_from_channel
- : tr::lng_group_call_remove_channel)(
- tr::now,
- lt_channel,
- participantPeer->name())
- : (_peer->isBroadcast()
- ? tr::lng_profile_sure_kick_channel
- : tr::lng_profile_sure_kick)(
- tr::now,
- lt_user,
- participantPeer->asUser()->firstName)),
- st::groupCallBoxLabel),
- style::margins(
- st::boxRowPadding.left(),
- st::boxPadding.top(),
- st::boxRowPadding.right(),
- st::boxPadding.bottom()));
- box->addButton(tr::lng_box_remove(), [=] {
- box->closeBox();
- kickParticipantSure(participantPeer);
- });
- box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
- }));
- }
- void Panel::showBox(object_ptr<Ui::BoxContent> box) {
- showBox(std::move(box), Ui::LayerOption::KeepOther, anim::type::normal);
- }
- void Panel::showBox(
- object_ptr<Ui::BoxContent> box,
- Ui::LayerOptions options,
- anim::type animated) {
- hideStickedTooltip(StickedTooltipHide::Unavailable);
- if (window()->width() < st::groupCallWidth
- || window()->height() < st::groupCallWidth) {
- window()->resize(
- std::max(window()->width(), st::groupCallWidth),
- std::max(window()->height(), st::groupCallWidth));
- }
- _layerBg->showBox(std::move(box), options, animated);
- }
- void Panel::showLayer(
- std::unique_ptr<Ui::LayerWidget> layer,
- Ui::LayerOptions options,
- anim::type animated) {
- hideStickedTooltip(StickedTooltipHide::Unavailable);
- if (window()->width() < st::groupCallWidth
- || window()->height() < st::groupCallWidth) {
- window()->resize(
- std::max(window()->width(), st::groupCallWidth),
- std::max(window()->height(), st::groupCallWidth));
- }
- _layerBg->showLayer(std::move(layer), options, animated);
- }
- void Panel::hideLayer(anim::type animated) {
- _layerBg->hideAll(animated);
- }
- bool Panel::isLayerShown() const {
- return _layerBg->topShownLayer() != nullptr;
- }
- void Panel::kickParticipantSure(not_null<PeerData*> participantPeer) {
- if (const auto chat = _peer->asChat()) {
- chat->session().api().chatParticipants().kick(chat, participantPeer);
- } else if (const auto channel = _peer->asChannel()) {
- const auto currentRestrictedRights = [&] {
- const auto user = participantPeer->asUser();
- if (!channel->mgInfo || !user) {
- return ChatRestrictionsInfo();
- }
- const auto i = channel->mgInfo->lastRestricted.find(user);
- return (i != channel->mgInfo->lastRestricted.cend())
- ? i->second.rights
- : ChatRestrictionsInfo();
- }();
- channel->session().api().chatParticipants().kick(
- channel,
- participantPeer,
- currentRestrictedRights);
- }
- }
- void Panel::initLayout() {
- initGeometry();
- #ifndef Q_OS_MAC
- _controls->wrap.raise();
- _controls->controls.layout().changes(
- ) | rpl::start_with_next([=] {
- // _menuToggle geometry depends on _controls arrangement.
- crl::on_main(widget(), [=] { updateControlsGeometry(); });
- }, lifetime());
- raiseControls();
- #endif // !Q_OS_MAC
- }
- void Panel::showControls() {
- Expects(_call != nullptr);
- widget()->showChildren();
- }
- void Panel::closeBeforeDestroy() {
- window()->close();
- _callLifetime.destroy();
- }
- rpl::lifetime &Panel::lifetime() {
- return window()->lifetime();
- }
- void Panel::initGeometry() {
- const auto center = Core::App().getPointForCallPanelCenter();
- const auto width = _call->rtmp()
- ? st::groupCallWidthRtmp
- : st::groupCallWidth;
- const auto height = _call->rtmp()
- ? st::groupCallHeightRtmp
- : st::groupCallHeight;
- const auto minWidth = _call->rtmp()
- ? st::groupCallWidthRtmpMin
- : st::groupCallWidth;
- const auto minHeight = _call->rtmp()
- ? st::groupCallHeightRtmpMin
- : st::groupCallHeight;
- const auto rect = QRect(0, 0, width, height);
- window()->setGeometry(rect.translated(center - rect.center()));
- window()->setMinimumSize({ minWidth, minHeight });
- window()->show();
- }
- QRect Panel::computeTitleRect() const {
- const auto skip = st::groupCallTitleSeparator;
- const auto remove = skip
- + (_menuToggle
- ? (_menuToggle->width() + st::groupCallMenuTogglePosition.x())
- : 0)
- + (_joinAsToggle
- ? (_joinAsToggle->width() + st::groupCallMenuTogglePosition.x())
- : 0)
- + (_pinOnTop
- ? (_pinOnTop->width() + skip)
- : 0);
- const auto width = widget()->width();
- #ifdef Q_OS_MAC
- return QRect(70, 0, width - remove - 70, 28);
- #else // Q_OS_MAC
- const auto controls = _controls->controls.geometry();
- const auto right = controls.x() + controls.width() + skip;
- return (controls.center().x() < width / 2)
- ? QRect(right, 0, width - right - remove, controls.height())
- : QRect(remove, 0, controls.x() - skip - remove, controls.height());
- #endif // !Q_OS_MAC
- }
- bool Panel::updateMode() {
- if (!_viewport) {
- return false;
- }
- const auto wide = _call->rtmp()
- || (_call->hasVideoWithFrames()
- && (widget()->width() >= st::groupCallWideModeWidthMin));
- const auto mode = wide ? PanelMode::Wide : PanelMode::Default;
- if (_mode.current() == mode) {
- return false;
- }
- if (!wide && _call->videoEndpointLarge()) {
- _call->showVideoEndpointLarge({});
- }
- refreshVideoButtons(wide);
- if (!_stickedTooltipClose
- || _niceTooltipControl.data() != _mute->outer().get()) {
- _niceTooltip.destroy();
- }
- _mode = mode;
- refreshTitleColors();
- if (wide && _subtitle) {
- _subtitle.destroy();
- } else if (!wide && !_subtitle) {
- refreshTitle();
- } else if (!_members) {
- setupMembers();
- }
- _wideControlsShown = _showWideControls = true;
- _wideControlsAnimation.stop();
- _viewport->widget()->setVisible(wide);
- if (_members) {
- _members->setMode(mode);
- }
- updateButtonsStyles();
- refreshControlsBackground();
- updateControlsGeometry();
- showStickedTooltip();
- return true;
- }
- void Panel::updateButtonsStyles() {
- const auto wide = (_mode.current() == PanelMode::Wide);
- _mute->setStyle(wide ? st::callMuteButtonSmall : st::callMuteButton);
- if (_video) {
- _video->setStyle(
- wide ? st::groupCallVideoSmall : st::groupCallVideo,
- (wide
- ? &st::groupCallVideoActiveSmall
- : &st::groupCallVideoActive));
- _video->setText(wide
- ? rpl::single(QString())
- : tr::lng_group_call_video());
- }
- if (_settings) {
- _settings->setText(wide
- ? rpl::single(QString())
- : tr::lng_group_call_settings());
- _settings->setStyle(wide
- ? st::groupCallSettingsSmall
- : st::groupCallSettings);
- }
- _hangup->setText(wide
- ? rpl::single(QString())
- : _call->scheduleDate()
- ? tr::lng_group_call_close()
- : tr::lng_group_call_leave());
- _hangup->setStyle(wide
- ? st::groupCallHangupSmall
- : st::groupCallHangup);
- }
- void Panel::setupEmptyRtmp() {
- _call->emptyRtmpValue(
- ) | rpl::start_with_next([=](bool empty) {
- if (!empty) {
- _emptyRtmp.destroy();
- return;
- } else if (_emptyRtmp) {
- return;
- }
- struct Label {
- Label(
- QWidget *parent,
- rpl::producer<QString> text,
- const style::color &color)
- : widget(parent, std::move(text), st::groupCallVideoLimitLabel)
- , corners(st::groupCallControlsBackRadius, color) {
- }
- Ui::FlatLabel widget;
- Ui::RoundRect corners;
- };
- _emptyRtmp.create(widget());
- const auto label = _emptyRtmp->lifetime().make_state<Label>(
- _emptyRtmp.data(),
- (_call->rtmpInfo().url.isEmpty()
- ? tr::lng_group_call_no_stream(
- lt_group,
- rpl::single(_peer->name()))
- : tr::lng_group_call_no_stream_admin()),
- _controlsBackgroundColor.color());
- _emptyRtmp->setAttribute(Qt::WA_TransparentForMouseEvents);
- _emptyRtmp->show();
- _emptyRtmp->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = QPainter(_emptyRtmp.data());
- label->corners.paint(p, _emptyRtmp->rect());
- }, _emptyRtmp->lifetime());
- widget()->sizeValue(
- ) | rpl::start_with_next([=](QSize size) {
- const auto padding = st::groupCallWidth / 30;
- const auto width = std::min(
- size.width() - padding * 4,
- st::groupCallWidth);
- label->widget.resizeToWidth(width);
- label->widget.move(padding, padding);
- _emptyRtmp->resize(
- width + 2 * padding,
- label->widget.height() + 2 * padding);
- _emptyRtmp->move(
- (size.width() - _emptyRtmp->width()) / 2,
- (size.height() - _emptyRtmp->height()) / 3);
- }, _emptyRtmp->lifetime());
- raiseControls();
- }, lifetime());
- }
- void Panel::refreshControlsBackground() {
- if (!_members) {
- return;
- }
- if (mode() == PanelMode::Default) {
- trackControls(false);
- _controlsBackgroundWide.destroy();
- if (_controlsBackgroundNarrow) {
- return;
- }
- setupControlsBackgroundNarrow();
- } else {
- _controlsBackgroundNarrow = nullptr;
- if (_controlsBackgroundWide) {
- return;
- }
- setupControlsBackgroundWide();
- }
- raiseControls();
- updateButtonsGeometry();
- }
- void Panel::refreshTitleBackground() {
- if (!_fullScreenOrMaximized.current()) {
- _titleBackground.destroy();
- return;
- } else if (_titleBackground) {
- return;
- }
- _titleBackground.create(widget());
- _titleBackground->show();
- raiseControls();
- auto &lifetime = _titleBackground->lifetime();
- const auto corners = lifetime.make_state<Ui::RoundRect>(
- st::roundRadiusLarge,
- _controlsBackgroundColor.color());
- _titleBackground->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = QPainter(_titleBackground.data());
- corners->paintSomeRounded(
- p,
- _titleBackground->rect(),
- RectPart::FullBottom);
- }, lifetime);
- refreshTitleGeometry();
- }
- void Panel::setupControlsBackgroundNarrow() {
- _controlsBackgroundNarrow = std::make_unique<ControlsBackgroundNarrow>(
- widget());
- _controlsBackgroundNarrow->shadow.show();
- _controlsBackgroundNarrow->blocker.show();
- auto &lifetime = _controlsBackgroundNarrow->shadow.lifetime();
- const auto factor = style::DevicePixelRatio();
- const auto height = std::max(
- st::groupCallMembersShadowHeight,
- st::groupCallMembersFadeSkip + st::groupCallMembersFadeHeight);
- const auto full = lifetime.make_state<QImage>(
- QSize(1, height * factor),
- QImage::Format_ARGB32_Premultiplied);
- rpl::single(rpl::empty) | rpl::then(
- style::PaletteChanged()
- ) | rpl::start_with_next([=] {
- full->fill(Qt::transparent);
- auto p = QPainter(full);
- const auto bottom = (height - st::groupCallMembersFadeSkip) * factor;
- p.fillRect(
- 0,
- bottom,
- full->width(),
- st::groupCallMembersFadeSkip * factor,
- st::groupCallMembersBg);
- p.drawImage(
- QRect(
- 0,
- bottom - (st::groupCallMembersFadeHeight * factor),
- full->width(),
- st::groupCallMembersFadeHeight * factor),
- Images::GenerateShadow(
- st::groupCallMembersFadeHeight,
- 0,
- 255,
- st::groupCallMembersBg->c));
- p.drawImage(
- QRect(
- 0,
- (height - st::groupCallMembersShadowHeight) * factor,
- full->width(),
- st::groupCallMembersShadowHeight * factor),
- Images::GenerateShadow(
- st::groupCallMembersShadowHeight,
- 0,
- 255,
- st::groupCallBg->c));
- }, lifetime);
- _controlsBackgroundNarrow->shadow.resize(
- (widget()->width()
- - st::groupCallMembersMargin.left()
- - st::groupCallMembersMargin.right()),
- height);
- _controlsBackgroundNarrow->shadow.paintRequest(
- ) | rpl::start_with_next([=](QRect clip) {
- auto p = QPainter(&_controlsBackgroundNarrow->shadow);
- clip = clip.intersected(_controlsBackgroundNarrow->shadow.rect());
- const auto inner = _members->getInnerGeometry().translated(
- _members->x() - _controlsBackgroundNarrow->shadow.x(),
- _members->y() - _controlsBackgroundNarrow->shadow.y());
- const auto faded = clip.intersected(inner);
- if (!faded.isEmpty()) {
- const auto factor = style::DevicePixelRatio();
- p.drawImage(
- faded,
- *full,
- QRect(
- 0,
- faded.y() * factor,
- full->width(),
- faded.height() * factor));
- }
- const auto bottom = inner.y() + inner.height();
- const auto after = clip.intersected(QRect(
- 0,
- bottom,
- inner.width(),
- _controlsBackgroundNarrow->shadow.height() - bottom));
- if (!after.isEmpty()) {
- p.fillRect(after, st::groupCallBg);
- }
- }, lifetime);
- _controlsBackgroundNarrow->shadow.setAttribute(
- Qt::WA_TransparentForMouseEvents);
- _controlsBackgroundNarrow->blocker.setUpdatesEnabled(false);
- }
- void Panel::setupControlsBackgroundWide() {
- _controlsBackgroundWide.create(widget());
- _controlsBackgroundWide->show();
- auto &lifetime = _controlsBackgroundWide->lifetime();
- const auto corners = lifetime.make_state<Ui::RoundRect>(
- st::groupCallControlsBackRadius,
- _controlsBackgroundColor.color());
- _controlsBackgroundWide->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = QPainter(_controlsBackgroundWide.data());
- corners->paint(p, _controlsBackgroundWide->rect());
- }, lifetime);
- trackControls(true);
- }
- void Panel::trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime) {
- if (!widget) {
- return;
- }
- widget->events(
- ) | rpl::start_with_next([=](not_null<QEvent*> e) {
- if (e->type() == QEvent::Enter) {
- trackControlOver(widget, true);
- } else if (e->type() == QEvent::Leave) {
- trackControlOver(widget, false);
- }
- }, lifetime);
- }
- void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
- if (_fullScreenOrMaximized.current()) {
- return;
- } else if (_stickedTooltipClose) {
- if (!over) {
- return;
- }
- } else {
- hideNiceTooltip();
- }
- if (over) {
- Ui::Integration::Instance().registerLeaveSubscription(control);
- showNiceTooltip(control);
- } else {
- Ui::Integration::Instance().unregisterLeaveSubscription(control);
- }
- toggleWideControls(over);
- }
- void Panel::showStickedTooltip() {
- static const auto kHasCamera = !Core::App().mediaDevices().defaultId(
- Webrtc::DeviceType::Camera).isEmpty();
- const auto callReady = (_call->state() == State::Joined
- || _call->state() == State::Connecting);
- if (!(_stickedTooltipsShown & StickedTooltip::Camera)
- && callReady
- && (_mode.current() == PanelMode::Wide)
- && _video
- && _call->videoIsWorking()
- && !_call->mutedByAdmin()
- && kHasCamera) { // Don't recount this every time for now.
- showNiceTooltip(_video, NiceTooltipType::Sticked);
- return;
- }
- hideStickedTooltip(
- StickedTooltip::Camera,
- StickedTooltipHide::Unavailable);
- if (!(_stickedTooltipsShown & StickedTooltip::Microphone)
- && callReady
- && _mute
- && !_call->mutedByAdmin()
- && !_layerBg->topShownLayer()) {
- if (_stickedTooltipClose) {
- // Showing already.
- return;
- } else if (!_micLevelTester) {
- // Check if there is incoming sound.
- _micLevelTester = std::make_unique<MicLevelTester>([=] {
- showStickedTooltip();
- });
- }
- if (_micLevelTester->showTooltip()) {
- _micLevelTester = nullptr;
- showNiceTooltip(_mute->outer(), NiceTooltipType::Sticked);
- }
- return;
- }
- _micLevelTester = nullptr;
- hideStickedTooltip(
- StickedTooltip::Microphone,
- StickedTooltipHide::Unavailable);
- }
- void Panel::showNiceTooltip(
- not_null<Ui::RpWidget*> control,
- NiceTooltipType type) {
- auto text = [&]() -> rpl::producer<QString> {
- if (control == _screenShare.data()) {
- if (_call->mutedByAdmin()) {
- return nullptr;
- }
- return tr::lng_group_call_tooltip_screen();
- } else if (control == _video.data()) {
- if (_call->mutedByAdmin()) {
- return nullptr;
- }
- return _call->isSharingCameraValue(
- ) | rpl::map([=](bool sharing) {
- return sharing
- ? tr::lng_group_call_tooltip_camera_off()
- : tr::lng_group_call_tooltip_camera();
- }) | rpl::flatten_latest();
- } else if (control == _settings.data()) {
- return tr::lng_group_call_settings();
- } else if (control == _mute->outer()) {
- return MuteButtonTooltip(_call);
- } else if (control == _hangup.data()) {
- return tr::lng_group_call_leave();
- }
- return rpl::producer<QString>();
- }();
- if (!text || _stickedTooltipClose) {
- return;
- } else if (_wideControlsAnimation.animating() || !_wideControlsShown) {
- if (type == NiceTooltipType::Normal) {
- return;
- }
- }
- const auto inner = [&]() -> Ui::RpWidget* {
- const auto normal = (type == NiceTooltipType::Normal);
- auto container = normal
- ? nullptr
- : Ui::CreateChild<Ui::RpWidget>(widget().get());
- const auto label = Ui::CreateChild<Ui::FlatLabel>(
- (normal ? widget().get() : container),
- std::move(text),
- st::groupCallNiceTooltipLabel);
- label->resizeToWidth(label->textMaxWidth());
- if (normal) {
- return label;
- }
- const auto button = Ui::CreateChild<Ui::IconButton>(
- container,
- st::groupCallStickedTooltipClose);
- rpl::combine(
- label->sizeValue(),
- button->sizeValue()
- ) | rpl::start_with_next([=](QSize text, QSize close) {
- const auto height = std::max(text.height(), close.height());
- container->resize(text.width() + close.width(), height);
- label->move(0, (height - text.height()) / 2);
- button->move(text.width(), (height - close.height()) / 2);
- }, container->lifetime());
- button->setClickedCallback([=] {
- hideStickedTooltip(StickedTooltipHide::Discarded);
- });
- _stickedTooltipClose = button;
- updateWideControlsVisibility();
- return container;
- }();
- _niceTooltip.create(
- widget().get(),
- object_ptr<Ui::RpWidget>::fromRaw(inner),
- (type == NiceTooltipType::Sticked
- ? st::groupCallStickedTooltip
- : st::groupCallNiceTooltip));
- const auto tooltip = _niceTooltip.data();
- const auto weak = QPointer<QWidget>(tooltip);
- const auto destroy = [=] {
- delete weak.data();
- };
- if (type != NiceTooltipType::Sticked) {
- tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
- }
- tooltip->setHiddenCallback(destroy);
- base::qt_signal_producer(
- control.get(),
- &QObject::destroyed
- ) | rpl::start_with_next(destroy, tooltip->lifetime());
- _niceTooltipControl = control;
- updateTooltipGeometry();
- tooltip->toggleAnimated(true);
- }
- void Panel::updateTooltipGeometry() {
- if (!_niceTooltip) {
- return;
- } else if (!_niceTooltipControl) {
- hideNiceTooltip();
- return;
- }
- const auto geometry = _niceTooltipControl->geometry();
- const auto weak = QPointer<QWidget>(_niceTooltip);
- const auto countPosition = [=](QSize size) {
- const auto strong = weak.data();
- const auto wide = (_mode.current() == PanelMode::Wide);
- const auto top = geometry.y()
- - (wide ? st::groupCallNiceTooltipTop : 0)
- - size.height();
- const auto middle = geometry.center().x();
- if (!strong) {
- return QPoint();
- } else if (!wide) {
- return QPoint(
- std::max(
- std::min(
- middle - size.width() / 2,
- (widget()->width()
- - st::groupCallMembersMargin.right()
- - size.width())),
- st::groupCallMembersMargin.left()),
- top);
- }
- const auto back = _controlsBackgroundWide.data();
- if (size.width() >= _viewport->widget()->width()) {
- return QPoint(_viewport->widget()->x(), top);
- } else if (back && size.width() >= back->width()) {
- return QPoint(
- back->x() - (size.width() - back->width()) / 2,
- top);
- } else if (back && (middle - back->x() < size.width() / 2)) {
- return QPoint(back->x(), top);
- } else if (back
- && (back->x() + back->width() - middle < size.width() / 2)) {
- return QPoint(back->x() + back->width() - size.width(), top);
- } else {
- return QPoint(middle - size.width() / 2, top);
- }
- };
- _niceTooltip->pointAt(geometry, RectPart::Top, countPosition);
- }
- void Panel::trackControls(bool track, bool force) {
- if (!force && _trackControls == track) {
- return;
- }
- _trackControls = track;
- _trackControlsOverStateLifetime.destroy();
- _trackControlsMenuLifetime.destroy();
- if (!track) {
- toggleWideControls(true);
- if (_wideControlsAnimation.animating()) {
- _wideControlsAnimation.stop();
- updateButtonsGeometry();
- }
- return;
- }
- const auto trackOne = [=](auto &&widget) {
- trackControl(widget, _trackControlsOverStateLifetime);
- };
- trackOne(_mute->outer());
- trackOne(_video);
- trackOne(_screenShare);
- trackOne(_wideMenu);
- trackOne(_settings);
- trackOne(_hangup);
- trackOne(_controlsBackgroundWide);
- trackControl(_menu, _trackControlsMenuLifetime);
- }
- void Panel::updateControlsGeometry() {
- if (widget()->size().isEmpty() || (!_settings && !_callShare)) {
- return;
- }
- updateButtonsGeometry();
- updateMembersGeometry();
- refreshTitle();
- #ifdef Q_OS_MAC
- const auto controlsOnTheLeft = true;
- const auto controlsPadding = 0;
- #else // Q_OS_MAC
- const auto center = _controls->controls.geometry().center();
- const auto controlsOnTheLeft = center.x()
- < widget()->width() / 2;
- const auto controlsPadding = _controls->wrap.y();
- #endif // Q_OS_MAC
- const auto menux = st::groupCallMenuTogglePosition.x();
- const auto menuy = st::groupCallMenuTogglePosition.y();
- if (controlsOnTheLeft) {
- if (_pinOnTop) {
- _pinOnTop->moveToRight(controlsPadding, controlsPadding);
- }
- if (_menuToggle) {
- _menuToggle->moveToRight(menux, menuy);
- } else if (_joinAsToggle) {
- _joinAsToggle->moveToRight(menux, menuy);
- }
- } else {
- if (_pinOnTop) {
- _pinOnTop->moveToLeft(controlsPadding, controlsPadding);
- }
- if (_menuToggle) {
- _menuToggle->moveToLeft(menux, menuy);
- } else if (_joinAsToggle) {
- _joinAsToggle->moveToLeft(menux, menuy);
- }
- }
- }
- void Panel::updateButtonsGeometry() {
- if (widget()->size().isEmpty() || (!_settings && !_callShare)) {
- return;
- }
- const auto toggle = [](auto &widget, bool shown) {
- if (widget && widget->isHidden() == shown) {
- widget->setVisible(shown);
- }
- };
- if (mode() == PanelMode::Wide) {
- Assert(_video != nullptr);
- Assert(_screenShare != nullptr);
- Assert(_wideMenu != nullptr);
- Assert(_settings != nullptr);
- Assert(_callShare == nullptr);
- const auto rtmp = _call->rtmp();
- const auto shown = _wideControlsAnimation.value(
- _wideControlsShown ? 1. : 0.);
- const auto hidden = (shown == 0.);
- if (_viewport) {
- _viewport->setControlsShown(rtmp ? 0. : shown);
- }
- const auto buttonsTop = widget()->height() - anim::interpolate(
- 0,
- st::groupCallButtonBottomSkipWide,
- shown);
- const auto addSkip = st::callMuteButtonSmall.active.outerRadius;
- const auto muteSize = _mute->innerSize().width() + 2 * addSkip;
- const auto skip = st::groupCallButtonSkipSmall;
- const auto fullWidth = (rtmp ? 0 : (_video->width() + skip))
- + (rtmp ? 0 : (_screenShare->width() + skip))
- + (muteSize + skip)
- + (_settings->width() + skip)
- + _hangup->width();
- const auto membersSkip = st::groupCallNarrowSkip;
- const auto membersWidth = _call->rtmp()
- ? membersSkip
- : (st::groupCallNarrowMembersWidth + 2 * membersSkip);
- auto left = membersSkip + (widget()->width()
- - membersWidth
- - membersSkip
- - fullWidth) / 2;
- toggle(_screenShare, !hidden && !rtmp);
- if (!rtmp) {
- _screenShare->moveToLeft(left, buttonsTop);
- left += _screenShare->width() + skip;
- }
- toggle(_video, !hidden && !rtmp);
- if (!rtmp) {
- _video->moveToLeft(left, buttonsTop);
- left += _video->width() + skip;
- } else {
- _wideMenu->moveToLeft(left, buttonsTop);
- _settings->moveToLeft(left, buttonsTop);
- left += _settings->width() + skip;
- }
- toggle(_mute, !hidden);
- _mute->moveInner({ left + addSkip, buttonsTop + addSkip });
- left += muteSize + skip;
- const auto wideMenuShown = _call->canManage()
- || _call->showChooseJoinAs();
- toggle(_settings, !hidden && !wideMenuShown);
- toggle(_wideMenu, !hidden && wideMenuShown);
- if (!rtmp) {
- _wideMenu->moveToLeft(left, buttonsTop);
- _settings->moveToLeft(left, buttonsTop);
- left += _settings->width() + skip;
- }
- toggle(_hangup, !hidden);
- _hangup->moveToLeft(left, buttonsTop);
- left += _hangup->width();
- if (_controlsBackgroundWide) {
- const auto rect = QRect(
- left - fullWidth,
- buttonsTop,
- fullWidth,
- _hangup->height());
- _controlsBackgroundWide->setGeometry(
- rect.marginsAdded(st::groupCallControlsBackMargin));
- }
- if (_fullScreenOrMaximized.current()) {
- refreshTitleGeometry();
- }
- } else {
- const auto muteTop = widget()->height()
- - st::groupCallMuteBottomSkip;
- const auto buttonsTop = widget()->height()
- - st::groupCallButtonBottomSkip;
- const auto muteSize = _mute->innerSize().width();
- const auto fullWidth = muteSize
- + 2 * (_settings ? _settings : _callShare)->width()
- + 2 * st::groupCallButtonSkip;
- toggle(_mute, true);
- _mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
- const auto leftButtonLeft = (widget()->width() - fullWidth) / 2;
- toggle(_screenShare, false);
- toggle(_wideMenu, false);
- toggle(_callShare, true);
- if (_callShare) {
- _callShare->moveToLeft(leftButtonLeft, buttonsTop);
- }
- const auto showVideoButton = videoButtonInNarrowMode();
- toggle(_video, !_callShare && showVideoButton);
- if (_video) {
- _video->setStyle(st::groupCallVideo, &st::groupCallVideoActive);
- _video->moveToLeft(leftButtonLeft, buttonsTop);
- }
- toggle(_settings, !_callShare && !showVideoButton);
- if (_settings) {
- _settings->moveToLeft(leftButtonLeft, buttonsTop);
- }
- toggle(_hangup, true);
- _hangup->moveToRight(leftButtonLeft, buttonsTop);
- }
- if (_controlsBackgroundNarrow) {
- const auto left = st::groupCallMembersMargin.left();
- const auto width = (widget()->width()
- - st::groupCallMembersMargin.left()
- - st::groupCallMembersMargin.right());
- _controlsBackgroundNarrow->shadow.setGeometry(
- left,
- (widget()->height()
- - st::groupCallMembersMargin.bottom()
- - _controlsBackgroundNarrow->shadow.height()),
- width,
- _controlsBackgroundNarrow->shadow.height());
- _controlsBackgroundNarrow->blocker.setGeometry(
- left,
- (widget()->height()
- - st::groupCallMembersMargin.bottom()
- - st::groupCallMembersBottomSkip),
- width,
- st::groupCallMembersBottomSkip);
- }
- updateTooltipGeometry();
- }
- bool Panel::videoButtonInNarrowMode() const {
- return (_video != nullptr) && !_call->mutedByAdmin();
- }
- void Panel::updateMembersGeometry() {
- if (!_members) {
- return;
- }
- _members->setVisible(!_call->rtmp());
- const auto desiredHeight = _members->desiredHeight();
- if (mode() == PanelMode::Wide) {
- const auto full = _fullScreenOrMaximized.current();
- const auto skip = full ? 0 : st::groupCallNarrowSkip;
- const auto membersWidth = st::groupCallNarrowMembersWidth;
- const auto top = full ? 0 : st::groupCallWideVideoTop;
- _members->setGeometry(
- widget()->width() - skip - membersWidth,
- top,
- membersWidth,
- std::min(desiredHeight, widget()->height() - top - skip));
- const auto viewportSkip = _call->rtmp()
- ? 0
- : (skip + membersWidth);
- _viewport->setGeometry(full, {
- skip,
- top,
- widget()->width() - viewportSkip - 2 * skip,
- widget()->height() - top - skip,
- });
- } else {
- const auto membersBottom = widget()->height();
- const auto membersTop = st::groupCallMembersTop;
- const auto availableHeight = membersBottom
- - st::groupCallMembersMargin.bottom()
- - membersTop;
- const auto membersWidthAvailable = widget()->width()
- - st::groupCallMembersMargin.left()
- - st::groupCallMembersMargin.right();
- const auto membersWidthMin = st::groupCallWidth
- - st::groupCallMembersMargin.left()
- - st::groupCallMembersMargin.right();
- const auto membersWidth = std::clamp(
- membersWidthAvailable,
- membersWidthMin,
- st::groupCallMembersWidthMax);
- _members->setGeometry(
- (widget()->width() - membersWidth) / 2,
- membersTop,
- membersWidth,
- std::min(desiredHeight, availableHeight));
- }
- }
- void Panel::refreshTitle() {
- if (!_title) {
- auto text = rpl::combine(
- Info::Profile::NameValue(_peer),
- rpl::single(
- QString()
- ) | rpl::then(_call->real(
- ) | rpl::map([=](not_null<Data::GroupCall*> real) {
- return real->titleValue();
- }) | rpl::flatten_latest())
- ) | rpl::map([=](const QString &name, const QString &title) {
- return title.isEmpty() ? name : title;
- }) | rpl::after_next([=] {
- refreshTitleGeometry();
- });
- _title.create(
- widget(),
- rpl::duplicate(text),
- st::groupCallTitleLabel);
- _title->show();
- _title->setAttribute(Qt::WA_TransparentForMouseEvents);
- if (_call->rtmp()) {
- _titleSeparator.create(
- widget(),
- rpl::single(QString::fromUtf8("\xE2\x80\xA2")),
- st::groupCallTitleLabel);
- _titleSeparator->show();
- _titleSeparator->setAttribute(Qt::WA_TransparentForMouseEvents);
- auto countText = _call->real(
- ) | rpl::map([=](not_null<Data::GroupCall*> real) {
- return tr::lng_group_call_rtmp_viewers(
- lt_count_decimal,
- real->fullCountValue(
- ) | rpl::map([=](int count) {
- return std::max(float64(count), 1.);
- }));
- }) | rpl::flatten_latest(
- ) | rpl::after_next([=] {
- refreshTitleGeometry();
- });
- _viewers.create(
- widget(),
- std::move(countText),
- st::groupCallTitleLabel);
- _viewers->show();
- _viewers->setAttribute(Qt::WA_TransparentForMouseEvents);
- }
- refreshTitleColors();
- style::PaletteChanged(
- ) | rpl::start_with_next([=] {
- refreshTitleColors();
- }, _title->lifetime());
- }
- refreshTitleGeometry();
- if (!_subtitle && mode() == PanelMode::Default) {
- _subtitle.create(
- widget(),
- rpl::single(
- _call->scheduleDate()
- ) | rpl::then(
- _call->real(
- ) | rpl::map([=](not_null<Data::GroupCall*> real) {
- return real->scheduleDateValue();
- }) | rpl::flatten_latest()
- ) | rpl::map([=](TimeId scheduleDate) {
- if (scheduleDate) {
- return tr::lng_group_call_scheduled_status();
- } else if (!_members) {
- setupMembers();
- }
- return tr::lng_group_call_members(
- lt_count_decimal,
- _members->fullCountValue() | rpl::map([](int value) {
- return (value > 0) ? float64(value) : 1.;
- }));
- }) | rpl::flatten_latest(),
- st::groupCallSubtitleLabel);
- _subtitle->show();
- _subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
- }
- if (_subtitle) {
- const auto top = _title
- ? st::groupCallSubtitleTop
- : st::groupCallTitleTop;
- _subtitle->moveToLeft(
- (widget()->width() - _subtitle->width()) / 2,
- top);
- }
- }
- void Panel::refreshTitleGeometry() {
- if (!_title) {
- return;
- }
- const auto fullRect = computeTitleRect();
- const auto titleRect = _recordingMark
- ? QRect(
- fullRect.x(),
- fullRect.y(),
- fullRect.width() - _recordingMark->width(),
- fullRect.height())
- : fullRect;
- const auto sep = st::groupCallTitleSeparator;
- const auto best = _title->textMaxWidth() + (_viewers
- ? (_titleSeparator->width() + sep * 2 + _viewers->textMaxWidth())
- : 0);
- const auto from = (widget()->width() - best) / 2;
- const auto shownTop = (mode() == PanelMode::Default)
- ? st::groupCallTitleTop
- : (st::groupCallWideVideoTop
- - st::groupCallTitleLabel.style.font->height) / 2;
- const auto shown = _fullScreenOrMaximized.current()
- ? _wideControlsAnimation.value(
- _wideControlsShown ? 1. : 0.)
- : 1.;
- const auto top = anim::interpolate(
- -_title->height() - st::boxRadius,
- shownTop,
- shown);
- const auto left = titleRect.x();
- const auto notEnough = std::max(0, best - titleRect.width());
- const auto titleMaxWidth = _title->textMaxWidth();
- const auto viewersMaxWidth = _viewers ? _viewers->textMaxWidth() : 0;
- const auto viewersNotEnough = std::clamp(
- viewersMaxWidth - titleMaxWidth,
- 0,
- notEnough
- ) + std::max(
- (notEnough - std::abs(viewersMaxWidth - titleMaxWidth)) / 2,
- 0);
- _title->resizeToWidth(
- _title->textMaxWidth() - (notEnough - viewersNotEnough));
- if (_viewers) {
- _viewers->resizeToWidth(_viewers->textMaxWidth() - viewersNotEnough);
- }
- const auto layout = [&](int position) {
- _title->moveToLeft(position, top);
- position += _title->width();
- if (_viewers) {
- _titleSeparator->moveToLeft(position + sep, top);
- position += sep + _titleSeparator->width() + sep;
- _viewers->moveToLeft(position, top);
- position += _viewers->width();
- }
- if (_recordingMark) {
- const auto markTop = top + st::groupCallRecordingMarkTop;
- _recordingMark->move(
- position,
- markTop - st::groupCallRecordingMarkSkip);
- }
- if (_titleBackground) {
- const auto bottom = _title->y()
- + _title->height()
- + (st::boxRadius / 2);
- const auto height = std::max(bottom, st::boxRadius * 2);
- _titleBackground->setGeometry(
- _title->x() - st::boxRadius,
- bottom - height,
- (position - _title->x()
- + st::boxRadius
- + (_recordingMark
- ? (_recordingMark->width() + st::boxRadius / 2)
- : st::boxRadius)),
- height);
- }
- };
- if (from >= left && from + best <= left + titleRect.width()) {
- layout(from);
- } else if (titleRect.width() < best) {
- layout(left);
- } else if (from < left) {
- layout(left);
- } else {
- layout(left + titleRect.width() - best);
- }
- #ifndef Q_OS_MAC
- _controlsTop = anim::interpolate(-_controls->wrap.height(), 0, shown);
- #endif // Q_OS_MAC
- }
- void Panel::refreshTitleColors() {
- if (!_title) {
- return;
- }
- auto gray = st::groupCallMemberNotJoinedStatus->c;
- const auto wide = (_mode.current() == PanelMode::Wide);
- _title->setTextColorOverride(wide
- ? std::make_optional(gray)
- : std::nullopt);
- if (_viewers) {
- _viewers->setTextColorOverride(gray);
- gray.setAlphaF(gray.alphaF() * 0.5);
- _titleSeparator->setTextColorOverride(gray);
- }
- }
- void Panel::paint(QRect clip) {
- auto p = QPainter(widget());
- auto region = QRegion(clip);
- for (const auto &rect : region) {
- p.fillRect(rect, st::groupCallBg);
- }
- }
- bool Panel::handleClose() {
- if (_call) {
- window()->hide();
- return true;
- }
- return false;
- }
- not_null<Ui::RpWindow*> Panel::window() const {
- return _window.window();
- }
- not_null<Ui::RpWidget*> Panel::widget() const {
- return _window.widget();
- }
- } // namespace Calls::Group
|