/* 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/calls_top_bar.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/wrap/padding_wrap.h" #include "lang/lang_keys.h" #include "calls/calls_call.h" #include "calls/calls_instance.h" #include "calls/calls_panel.h" #include "data/data_user.h" #include "data/data_changes.h" #include "main/main_session.h" #include "boxes/abstract_box.h" #include "base/timer.h" #include "layout.h" #include "app.h" #include "styles/style_calls.h" #include "styles/style_layers.h" namespace Calls { namespace { constexpr auto kUpdateDebugTimeoutMs = crl::time(500); class DebugInfoBox : public Ui::BoxContent { public: DebugInfoBox(QWidget*, base::weak_ptr call); protected: void prepare() override; private: void updateText(); base::weak_ptr _call; QPointer _text; base::Timer _updateTextTimer; }; DebugInfoBox::DebugInfoBox(QWidget*, base::weak_ptr call) : _call(call) { } void DebugInfoBox::prepare() { setTitle(rpl::single(qsl("Call Debug"))); addButton(tr::lng_close(), [this] { closeBox(); }); _text = setInnerWidget( object_ptr>( this, object_ptr(this, st::callDebugLabel), st::callDebugPadding))->entity(); _text->setSelectable(true); updateText(); _updateTextTimer.setCallback([this] { updateText(); }); _updateTextTimer.callEach(kUpdateDebugTimeoutMs); setDimensions(st::boxWideWidth, st::boxMaxListHeight); } void DebugInfoBox::updateText() { if (auto call = _call.get()) { _text->setText(call->getDebugLog()); } } } // namespace TopBar::TopBar( QWidget *parent, const base::weak_ptr &call) : RpWidget(parent) , _call(call) , _durationLabel(this, st::callBarLabel) , _signalBars(this, _call.get(), st::callBarSignalBars) , _fullInfoLabel(this, st::callBarInfoLabel) , _shortInfoLabel(this, st::callBarInfoLabel) , _hangupLabel(this, st::callBarLabel, tr::lng_call_bar_hangup(tr::now).toUpper()) , _mute(this, st::callBarMuteToggle) , _info(this) , _hangup(this, st::callBarHangup) { initControls(); resize(width(), st::callBarHeight); } void TopBar::initControls() { _mute->setClickedCallback([=] { if (const auto call = _call.get()) { call->setMute(!call->isMute()); } }); setMuted(_call->isMute()); subscribe(_call->muteChanged(), [=](bool mute) { setMuted(mute); update(); }); _call->user()->session().changes().peerUpdates( Data::PeerUpdate::Flag::Name ) | rpl::filter([=](const Data::PeerUpdate &update) { // _user may change for the same Panel. return (_call != nullptr) && (update.peer == _call->user()); }) | rpl::start_with_next([=] { updateInfoLabels(); }, lifetime()); setInfoLabels(); _info->setClickedCallback([=] { if (const auto call = _call.get()) { if (Logs::DebugEnabled() && (_info->clickModifiers() & Qt::ControlModifier)) { Ui::show(Box(_call)); } else { call->user()->session().calls().showInfoPanel(call); } } }); _hangup->setClickedCallback([this] { if (auto call = _call.get()) { call->hangup(); } }); _updateDurationTimer.setCallback([this] { updateDurationText(); }); updateDurationText(); } void TopBar::updateInfoLabels() { setInfoLabels(); updateControlsGeometry(); } void TopBar::setInfoLabels() { if (const auto call = _call.get()) { const auto user = call->user(); const auto fullName = user->name; const auto shortName = user->firstName; _fullInfoLabel->setText(fullName.toUpper()); _shortInfoLabel->setText(shortName.toUpper()); } } void TopBar::setMuted(bool mute) { _mute->setIconOverride(mute ? &st::callBarUnmuteIcon : nullptr); _mute->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr); _hangup->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr); _muted = mute; } void TopBar::updateDurationText() { if (!_call) { return; } auto wasWidth = _durationLabel->width(); auto durationMs = _call->getDurationMs(); auto durationSeconds = durationMs / 1000; startDurationUpdateTimer(durationMs); _durationLabel->setText(formatDurationText(durationSeconds)); if (_durationLabel->width() != wasWidth) { updateControlsGeometry(); } } void TopBar::startDurationUpdateTimer(crl::time currentDuration) { auto msTillNextSecond = 1000 - (currentDuration % 1000); _updateDurationTimer.callOnce(msTillNextSecond + 5); } void TopBar::resizeEvent(QResizeEvent *e) { updateControlsGeometry(); } void TopBar::updateControlsGeometry() { auto left = 0; _mute->moveToLeft(left, 0); left += _mute->width(); _durationLabel->moveToLeft(left, st::callBarLabelTop); left += _durationLabel->width() + st::callBarSkip; _signalBars->moveToLeft(left, (height() - _signalBars->height()) / 2); left += _signalBars->width() + st::callBarSkip; auto right = st::callBarRightSkip; _hangupLabel->moveToRight(right, st::callBarLabelTop); right += _hangupLabel->width(); right += st::callBarHangup.width; _hangup->setGeometryToRight(0, 0, right, height()); _info->setGeometryToLeft( _mute->width(), 0, width() - _mute->width() - _hangup->width(), height()); auto fullWidth = _fullInfoLabel->naturalWidth(); auto showFull = (left + fullWidth + right <= width()); _fullInfoLabel->setVisible(showFull); _shortInfoLabel->setVisible(!showFull); auto setInfoLabelGeometry = [this, left, right](auto &&infoLabel) { auto minPadding = qMax(left, right); auto infoWidth = infoLabel->naturalWidth(); auto infoLeft = (width() - infoWidth) / 2; if (infoLeft < minPadding) { infoLeft = left; infoWidth = width() - left - right; } infoLabel->setGeometryToLeft(infoLeft, st::callBarLabelTop, infoWidth, st::callBarInfoLabel.style.font->height); }; setInfoLabelGeometry(_fullInfoLabel); setInfoLabelGeometry(_shortInfoLabel); } void TopBar::paintEvent(QPaintEvent *e) { Painter p(this); p.fillRect(e->rect(), _muted ? st::callBarBgMuted : st::callBarBg); } TopBar::~TopBar() = default; } // namespace Calls