mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-06 15:24:26 +00:00
Add call panel show / hide animation.
This commit is contained in:
parent
299dc3fc96
commit
2214e980ef
@ -188,3 +188,4 @@ callRatingCommentTop: 2px;
|
||||
callDebugLabel: FlatLabel(defaultFlatLabel) {
|
||||
margin: margins(24px, 0px, 24px, 0px);
|
||||
}
|
||||
callPanelDuration: 200;
|
||||
|
@ -203,8 +203,7 @@ TimeMs Call::getDurationMs() const {
|
||||
|
||||
void Call::hangup() {
|
||||
if (_state == State::Busy) {
|
||||
// Cancel call instead of redial.
|
||||
setState(Call::Ended);
|
||||
_delegate->callFinished(this);
|
||||
} else {
|
||||
auto missed = (_state == State::Ringing || (_state == State::Waiting && _type == Type::Outgoing));
|
||||
auto declined = (_state == State::WaitingIncoming);
|
||||
@ -513,8 +512,6 @@ bool Call::checkCallFields(const MTPDphoneCallAccepted &call) {
|
||||
|
||||
void Call::setState(State state) {
|
||||
if (_state != state) {
|
||||
auto wasBusy = (_state == State::Busy);
|
||||
|
||||
_state = state;
|
||||
_stateChanged.notify(state, true);
|
||||
|
||||
@ -534,15 +531,11 @@ void Call::setState(State state) {
|
||||
_delegate->playSound(Delegate::Sound::Connecting);
|
||||
break;
|
||||
case State::Ended:
|
||||
if (!wasBusy) {
|
||||
_delegate->playSound(Delegate::Sound::Ended);
|
||||
}
|
||||
_delegate->playSound(Delegate::Sound::Ended);
|
||||
_delegate->callFinished(this);
|
||||
break;
|
||||
case State::Failed:
|
||||
if (!wasBusy) {
|
||||
_delegate->playSound(Delegate::Sound::Ended);
|
||||
}
|
||||
_delegate->playSound(Delegate::Sound::Ended);
|
||||
_delegate->callFailed(this);
|
||||
break;
|
||||
case State::Busy:
|
||||
|
@ -98,7 +98,7 @@ void Instance::playSound(Sound sound) {
|
||||
|
||||
void Instance::destroyCall(gsl::not_null<Call*> call) {
|
||||
if (_currentCall.get() == call) {
|
||||
_currentCallPanel.reset();
|
||||
destroyCurrentPanel();
|
||||
_currentCall.reset();
|
||||
_currentCallChanged.notify(nullptr, true);
|
||||
|
||||
@ -109,6 +109,14 @@ void Instance::destroyCall(gsl::not_null<Call*> call) {
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::destroyCurrentPanel() {
|
||||
_pendingPanels.erase(std::remove_if(_pendingPanels.begin(), _pendingPanels.end(), [](auto &&panel) {
|
||||
return !panel;
|
||||
}), _pendingPanels.end());
|
||||
_pendingPanels.push_back(_currentCallPanel.release());
|
||||
_pendingPanels.back()->hideAndDestroy(); // Always queues the destruction.
|
||||
}
|
||||
|
||||
void Instance::createCall(gsl::not_null<UserData*> user, Call::Type type) {
|
||||
auto call = std::make_unique<Call>(getCallDelegate(), user, type);;
|
||||
if (_currentCall) {
|
||||
@ -282,7 +290,13 @@ bool Instance::alreadyInCall() {
|
||||
return (_currentCall && _currentCall->state() != Call::State::Busy);
|
||||
}
|
||||
|
||||
Instance::~Instance() = default;
|
||||
Instance::~Instance() {
|
||||
for (auto panel : _pendingPanels) {
|
||||
if (panel) {
|
||||
delete panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instance &Current() {
|
||||
return AuthSession::Current().calls();
|
||||
|
@ -67,6 +67,7 @@ private:
|
||||
void playSound(Sound sound) override;
|
||||
void createCall(gsl::not_null<UserData*> user, Call::Type type);
|
||||
void destroyCall(gsl::not_null<Call*> call);
|
||||
void destroyCurrentPanel();
|
||||
|
||||
void refreshDhConfig();
|
||||
void refreshServerConfig();
|
||||
@ -83,6 +84,7 @@ private:
|
||||
std::unique_ptr<Panel> _currentCallPanel;
|
||||
base::Observable<Call*> _currentCallChanged;
|
||||
base::Observable<FullMsgId> _newServiceMessage;
|
||||
std::vector<QPointer<Panel>> _pendingPanels;
|
||||
|
||||
std::unique_ptr<Media::Audio::Track> _callConnectingTrack;
|
||||
std::unique_ptr<Media::Audio::Track> _callEndedTrack;
|
||||
|
@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "apiwrap.h"
|
||||
#include "observer_peer.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/task_queue.h"
|
||||
|
||||
namespace Calls {
|
||||
namespace {
|
||||
@ -145,7 +146,9 @@ void Panel::hideDeactivated() {
|
||||
|
||||
void Panel::initControls() {
|
||||
_mute->setClickedCallback([this] {
|
||||
_call->setMute(!_call->isMute());
|
||||
if (_call) {
|
||||
_call->setMute(!_call->isMute());
|
||||
}
|
||||
});
|
||||
subscribe(_call->muteChanged(), [this](bool mute) {
|
||||
_mute->setIconOverride(mute ? &st::callUnmuteIcon : nullptr);
|
||||
@ -167,6 +170,8 @@ void Panel::initControls() {
|
||||
}
|
||||
|
||||
void Panel::reinitControls() {
|
||||
Expects(_call != nullptr);
|
||||
|
||||
unsubscribe(_stateChangedSubscription);
|
||||
_stateChangedSubscription = subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); });
|
||||
stateChanged(_call->state());
|
||||
@ -180,7 +185,7 @@ void Panel::refreshCallbacks() {
|
||||
if (button) {
|
||||
button->setClickedCallback([this, callback] {
|
||||
if (_call) {
|
||||
callback(_call.get());
|
||||
callback(_call);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -209,10 +214,47 @@ void Panel::initLayout() {
|
||||
refreshUserPhoto();
|
||||
});
|
||||
createDefaultCacheImage();
|
||||
toggleOpacityAnimation(true);
|
||||
|
||||
Platform::InitOnTopPanel(this);
|
||||
}
|
||||
|
||||
void Panel::toggleOpacityAnimation(bool visible) {
|
||||
if (_useTransparency) {
|
||||
if (_animationCache.isNull()) {
|
||||
_animationCache = myGrab(this);
|
||||
hideChildren();
|
||||
}
|
||||
_opacityAnimation.start([this] { update(); }, visible ? 0. : 1., visible ? 1. : 0., st::callPanelDuration, visible ? anim::easeOutCirc : anim::easeInCirc);
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::finishAnimation() {
|
||||
_animationCache = QPixmap();
|
||||
if (_call) {
|
||||
showChildren();
|
||||
} else {
|
||||
destroyDelayed();
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::destroyDelayed() {
|
||||
hide();
|
||||
base::TaskQueue::Main().Put([weak = QPointer<Panel>(this)] {
|
||||
if (weak) {
|
||||
delete weak.data();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::hideAndDestroy() {
|
||||
toggleOpacityAnimation(false);
|
||||
_call = nullptr;
|
||||
if (_animationCache.isNull()) {
|
||||
destroyDelayed();
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::processUserPhoto() {
|
||||
if (!_user->userpicLoaded()) {
|
||||
_user->loadUserpic(true);
|
||||
@ -367,6 +409,23 @@ void Panel::updateStatusGeometry() {
|
||||
|
||||
void Panel::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
if (!_animationCache.isNull()) {
|
||||
auto opacity = _opacityAnimation.current(getms(), _call ? 1. : 0.);
|
||||
if (!_opacityAnimation.animating()) {
|
||||
finishAnimation();
|
||||
if (!_call) return;
|
||||
} else {
|
||||
p.setOpacity(opacity);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
auto marginRatio = (1. - opacity) / 5;
|
||||
auto marginWidth = qRound(width() * marginRatio);
|
||||
auto marginHeight = qRound(height() * marginRatio);
|
||||
p.drawPixmap(rect().marginsRemoved(QMargins(marginWidth, marginHeight, marginWidth, marginHeight)), _animationCache, QRect(QPoint(0, 0), _animationCache.size()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_useTransparency) {
|
||||
Platform::StartTranslucentPaint(p, e);
|
||||
p.drawPixmapLeft(0, 0, width(), _cache);
|
||||
@ -472,15 +531,16 @@ void Panel::stateChanged(State state) {
|
||||
syncButton(_hangup, (state != State::Busy), st::callHangup);
|
||||
syncButton(_redial, (state == State::Busy), st::callAnswer);
|
||||
syncButton(_cancel, (state == State::Busy), st::callCancel);
|
||||
|
||||
if (_fingerprint.empty() && _call->isKeyShaForFingerprintReady()) {
|
||||
fillFingerprint();
|
||||
}
|
||||
}
|
||||
if (buttonsUpdated) {
|
||||
refreshCallbacks();
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) {
|
||||
fillFingerprint();
|
||||
}
|
||||
if ((state == State::Starting) || (state == State::WaitingIncoming)) {
|
||||
Platform::ReInitOnTopPanel(this);
|
||||
} else {
|
||||
@ -494,7 +554,8 @@ void Panel::stateChanged(State state) {
|
||||
}
|
||||
|
||||
void Panel::fillFingerprint() {
|
||||
_fingerprint = ComputeEmojiFingerprint(_call.get());
|
||||
Expects(_call != nullptr);
|
||||
_fingerprint = ComputeEmojiFingerprint(_call);
|
||||
|
||||
auto realSize = Ui::Emoji::Size(Ui::Emoji::Index() + 1);
|
||||
auto size = realSize / cIntRetinaFactor();
|
||||
|
@ -37,8 +37,8 @@ public:
|
||||
Panel(gsl::not_null<Call*> call);
|
||||
|
||||
void showAndActivate();
|
||||
|
||||
void replaceCall(gsl::not_null<Call*> call);
|
||||
void hideAndDestroy();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
@ -80,8 +80,11 @@ private:
|
||||
void updateStatusText(State state);
|
||||
void startDurationUpdateTimer(TimeMs currentDuration);
|
||||
void fillFingerprint();
|
||||
void toggleOpacityAnimation(bool visible);
|
||||
void finishAnimation();
|
||||
void destroyDelayed();
|
||||
|
||||
base::weak_unique_ptr<Call> _call;
|
||||
Call *_call = nullptr;
|
||||
gsl::not_null<UserData*> _user;
|
||||
|
||||
bool _useTransparency = true;
|
||||
@ -112,8 +115,9 @@ private:
|
||||
PhotoId _userPhotoId = 0;
|
||||
bool _userPhotoFull = false;
|
||||
|
||||
Animation _opacityAnimation;
|
||||
QPixmap _animationCache;
|
||||
QPixmap _bottomCache;
|
||||
|
||||
QPixmap _cache;
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user